Compare commits

..

87 commits

Author SHA1 Message Date
Lea Anthony
d24d82f86d
Rename exp -> v3 2023-01-18 21:34:11 +11:00
Lea Anthony
81e9223124
Merge branch 'master' into future/exp 2023-01-18 21:27:30 +11:00
Lea Anthony
2374bb7e0e
Update clir 2023-01-18 20:57:31 +11:00
Lea Anthony
4c42ef9710
Create generate bindings command 2023-01-18 20:19:12 +11:00
stffabi
b4d64678b5 Basic integration of the assetserver 2023-01-18 09:31:09 +01:00
Lea Anthony
a4f729a6a0
Rename parser package.
Fix linting issues
Add README.md
2023-01-18 08:22:31 +11:00
Lea Anthony
8e593e9c35
Start of TS models generation support 2023-01-17 18:57:35 +11:00
Lea Anthony
0a09363db7
Support struct discovery over multiple files 2023-01-17 07:45:41 +11:00
Lea Anthony
b0b96c9838
Support struct discovery in methods 2023-01-16 20:30:05 +11:00
Lea Anthony
8bbf5ff3f1
Recursive bound model discovery implemented 2023-01-15 20:25:42 +11:00
Lea Anthony
572fbc9705
Initial parsing to discover bound struct names 2023-01-13 17:35:43 +11:00
Lea Anthony
f371b513d1
Fix name of Window menu in role 2023-01-13 17:33:54 +11:00
Lea Anthony
e5d3803fc1
Initial runtime builds
Added Taskfile for fast runtime builds
2023-01-08 21:06:31 +11:00
Lea Anthony
06a0377963
Fix frameless
Add rounded corners for frameless
Create NSWindow subclass
Add pre-post build hooks
2023-01-07 11:26:46 +11:00
Lea Anthony
62a5509390
Do not display internal tasks when using wails task list 2023-01-06 20:20:40 +11:00
Lea Anthony
54805d99a0
Remove New from Dialog methods 2023-01-06 20:19:20 +11:00
Lea Anthony
d1e2ee452f
Integrate Task 2023-01-05 15:02:08 +11:00
Lea Anthony
b5127d95ff
Add EnableFraudulentWebsiteWarnings feature
Co-authored-by: stffabi <stffabi@users.noreply.github.com>
2023-01-04 20:12:28 +11:00
Lea Anthony
174a31b49a
Add run command for running tasks in a Taskfile.yml
Add `generate` command for generating assets
Add build example
2023-01-04 20:01:13 +11:00
Lea Anthony
746b4dfc04
Fix sizes bug
Remove redundant menu package
2023-01-03 05:43:56 +11:00
Lea Anthony
e5da0eb5f8
Add icon generation
Add syso generation
Add tests
2023-01-02 21:23:44 +11:00
Lea Anthony
f4044ebcd9
Add icon tool 2023-01-02 08:23:29 +11:00
Lea Anthony
bc7d8295ba
Refactor window -> webviewwindow 2023-01-02 05:48:52 +11:00
Lea Anthony
71fb7fadff
Merge branch 'chore/move_assetserver_to_pkg' into future/exp 2022-12-31 13:08:34 +11:00
Lea Anthony
be240795e2
Refactor assetserver into pkg 2022-12-31 11:01:24 +11:00
Lea Anthony
cf5def05a6
Refactor application API to use options by default. Updated examples. 2022-12-31 10:38:36 +11:00
Lea Anthony
ad802a3ad0
Frameless support
applicationShouldTerminateAfterLastWindowClosed support
2022-12-30 14:14:28 +11:00
Lea Anthony
287039a8f3
Task experiment 2022-12-28 19:17:38 +11:00
Lea Anthony
ad1d104d85
Add screen support
Co-authored-by: Oleg Gulevskyy <43781031+oleggulevskyy@users.noreply.github.com>
2022-12-28 14:09:13 +11:00
Lea Anthony
92af7455ad
Support Show/Hide window
Better demo
window.Run() -> window.Show()
2022-12-27 21:15:43 +11:00
Lea Anthony
b6b6acf1d9
Remove debug printfs 2022-12-27 15:31:24 +11:00
Lea Anthony
5187641e25
Remove C.toggleFullscreen 2022-12-27 15:29:57 +11:00
Lea Anthony
41779866c4
Disable fullscreen button if there are size constraints 2022-12-27 13:42:57 +11:00
Lea Anthony
f800222781
Allow SetSize to adjust min/max constraints 2022-12-27 09:38:00 +11:00
Lea Anthony
0c364b2813
Fix sizing of window/contentrect
Fix set min/max size
Ignore SetSize when in fullscreen
Disable size constraints when going fullscreen
2022-12-27 09:23:54 +11:00
Lea Anthony
7dbb8c1377
Improve Minimise, Maximise
Add Restore
setAutoenablesItems:NO
2022-12-26 20:21:51 +11:00
Lea Anthony
6a0c101b08
Fix dialog ID issue
Use map instead of uint for dialog IDs
2022-12-24 20:40:34 +11:00
Lea Anthony
f240f2100c
Refactor mainthread dispatch 2022-12-24 15:35:04 +11:00
Lea Anthony
2b91ced1b7
Support SetPosition
Optimise event listener callbacks
2022-12-24 13:38:48 +11:00
Lea Anthony
b7f1defbf3
NavigateToURL -> SetURL
Fixed typo in colour
Removed EnableDevTools
2022-12-23 23:18:28 +11:00
Lea Anthony
2946a0aa25
GetCurrentWindow -> CurrentWindow 2022-12-23 19:55:54 +11:00
Lea Anthony
b043126031
Add open file dialog filters
Add clipboard
Fix mouse event handling
2022-12-23 07:34:12 +11:00
Lea Anthony
4d58b56d54
Improve dialogs 2022-12-21 21:30:26 +11:00
Lea Anthony
025e8d7645
Add SaveFileDialog
Add Toolbar Styles
Add Invisible Titlebar option with drag support
2022-12-20 21:49:19 +11:00
Lea Anthony
e7abe1c606
Add Webview navigation events
Add HTML/JS/CSS in config
Add RenderHTML()
Tidy up window delegate
2022-12-19 21:17:50 +11:00
Lea Anthony
ea1f086289
OpenDialog attach to window & multiple files selection 2022-12-19 08:26:59 +11:00
Lea Anthony
b1e8f2f887
Start of file dialogs 2022-12-18 23:06:31 +11:00
Lea Anthony
45adc49683
Add application icon support
Add message dialog support
Add default icons
Add png to byte slice task
Fixed kitchen sink
2022-12-18 13:06:03 +11:00
Lea Anthony
2409984aab
Add application about dialog
Add application icon support
2022-12-18 00:40:53 +11:00
Lea Anthony
8f9eeb8055
Improve menu demo 2022-12-17 20:49:58 +11:00
Lea Anthony
a8d5f185c2
Fix resizing bug
Refactor some application setters
2022-12-17 20:49:46 +11:00
Lea Anthony
bbb418f98f
Fix submenu radio processing 2022-12-17 20:49:04 +11:00
Lea Anthony
f8eb680559
Add Window & Help menus
Fix min/max sizing
2022-12-17 19:56:29 +11:00
Lea Anthony
da5fa806d7
Add Zoom In/Out & Actual size roles 2022-12-16 21:28:16 +11:00
Lea Anthony
88f6a3d1b9
Refactor and support more roles 2022-12-16 21:09:31 +11:00
Lea Anthony
475880c0a8
Add services, hide, hideothers, unhide role
Add application.Name()
refactor roles
Add app menu role
2022-12-16 21:06:49 +11:00
Lea Anthony
faf418eac0
Add global reference for application
Add default application meu for Mac
Add window example
Fix invalid accelerator error logging
2022-12-15 21:21:19 +11:00
Lea Anthony
f35b8dfec5
Refactor Application
Add SetActivationPolicy
Added labels to menus
2022-12-15 18:06:09 +11:00
Lea Anthony
14fcfb8348
Refactor event types 2022-12-15 18:03:01 +11:00
Lea Anthony
f84c4aca72
Refactor message processing functions 2022-12-15 08:01:49 +11:00
Lea Anthony
dad7798da9
New Systray example 2022-12-15 08:01:21 +11:00
Lea Anthony
79af667b7a
Support Menu Accelerators 2022-12-15 08:00:49 +11:00
Lea Anthony
d84cb5b190
Fix enabled menuitem 2022-12-14 19:15:58 +11:00
Lea Anthony
7c18f25508
Support radio menuitems
Support systemtray destroy()
2022-12-14 19:03:27 +11:00
Lea Anthony
085de606ee
Support submenus 2022-12-14 08:48:17 +11:00
Lea Anthony
c7ac90def4
Support template icons 2022-12-14 08:26:52 +11:00
Lea Anthony
bb36ce61bc
Refactor dispatching 2022-12-14 07:52:10 +11:00
Lea Anthony
a35a876094
Add support for system tray + menus 2022-12-13 21:54:12 +11:00
Lea Anthony
f38c6c6d1c
Add Fatal() 2022-12-11 20:05:00 +11:00
Lea Anthony
f6a16950f2
Add Window methods Width(), Height(), Size() + Position()
Fix main thread locking issue
Add Window aliases
2022-12-11 19:57:20 +11:00
Lea Anthony
ddb2f63f7b
Support Window aliases 2022-12-11 14:39:53 +11:00
Lea Anthony
bf8e541221
Support Window events
Convert events to uint
Rename system events to application events
2022-12-11 13:01:38 +11:00
Lea Anthony
4a2cf597c2
Fix resizable 2022-12-11 11:26:21 +11:00
Lea Anthony
27370559d3
Refactor Is* methods
Add more events
2022-12-11 11:09:52 +11:00
Lea Anthony
a134bfac42
Add Center
Add more events
2022-12-10 22:34:15 +11:00
Lea Anthony
d5e21d5003
Fix race condition in closing window during navigating
Add sane defaults
2022-12-09 08:22:17 +11:00
Lea Anthony
85abb1122f
Add main thread dispatching
Make `window.Run` operate on main thread
2022-12-09 07:47:47 +11:00
Lea Anthony
0257362a86
Support per-window message dispatching 2022-12-08 23:06:27 +11:00
Lea Anthony
1872672d0c
Support appearance 2022-12-08 18:20:04 +11:00
Lea Anthony
644655662e
Support toolbar config 2022-12-08 18:09:02 +11:00
Lea Anthony
508bbb4fb4
Support Min/Max/Fullscreen, BackgroundColour 2022-12-08 17:25:39 +11:00
Lea Anthony
cb9df61a98
Support MacBackdrop type
Fix devtools window error
2022-12-08 07:47:04 +11:00
Lea Anthony
ab976e56af
Support ExecJS 2022-12-07 23:58:45 +11:00
Lea Anthony
df1cc2ee0b
Support EnableDevTools 2022-12-07 23:52:16 +11:00
Lea Anthony
af6daa273e
Support min/max window size 2022-12-07 23:44:19 +11:00
Lea Anthony
05803fb5d5
Add more Window API 2022-12-07 23:30:20 +11:00
Lea Anthony
5a37091bb0
Initial play around 2022-12-07 21:55:09 +11:00
6390 changed files with 141969 additions and 412073 deletions

View file

@ -158,7 +158,7 @@
]
},
{
"login": "sircodemane",
"login": "codydbentley",
"name": "Cody Bentley",
"avatar_url": "https://avatars.githubusercontent.com/u/6968902?v=4",
"profile": "https://codybentley.dev/",
@ -1348,517 +1348,6 @@
"contributions": [
"doc"
]
},
{
"login": "github-actions[bot]",
"name": "github-actions[bot]",
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
"profile": "https://github.com/apps/github-actions",
"contributions": [
"code"
]
},
{
"login": "OlegGulevskyy",
"name": "Oleg Gulevskyy",
"avatar_url": "https://avatars.githubusercontent.com/u/43781031?v=4",
"profile": "https://github.com/OlegGulevskyy",
"contributions": [
"code",
"doc",
"maintenance",
"platform"
]
},
{
"login": "raguay",
"name": "Richard Guay",
"avatar_url": "https://avatars.githubusercontent.com/u/2487495?v=4",
"profile": "http://www.customct.com/",
"contributions": [
"doc"
]
},
{
"login": "ATenderholt",
"name": "Adam Tenderholt",
"avatar_url": "https://avatars.githubusercontent.com/u/740623?v=4",
"profile": "https://github.com/ATenderholt",
"contributions": [
"code"
]
},
{
"login": "JulioDRF",
"name": "JulioDRF",
"avatar_url": "https://avatars.githubusercontent.com/u/15677708?v=4",
"profile": "https://github.com/JulioDRF",
"contributions": [
"code"
]
},
{
"login": "scottopell",
"name": "Scott Opell",
"avatar_url": "https://avatars.githubusercontent.com/u/996472?v=4",
"profile": "http://scottopell.com/",
"contributions": [
"code"
]
},
{
"login": "avengerweb",
"name": "Vadim Shchepotev",
"avatar_url": "https://avatars.githubusercontent.com/u/2055581?v=4",
"profile": "https://aven.dev/",
"contributions": [
"code"
]
},
{
"login": "willdot",
"name": "Will Andrews",
"avatar_url": "https://avatars.githubusercontent.com/u/4906530?v=4",
"profile": "https://willdot.net/",
"contributions": [
"code"
]
},
{
"login": "gwynforthewyn",
"name": "Gwyn",
"avatar_url": "https://avatars.githubusercontent.com/u/434656?v=4",
"profile": "https://github.com/gwynforthewyn",
"contributions": [
"code",
"review",
"question",
"research"
]
},
{
"login": "xijaja",
"name": "希嘉嘉",
"avatar_url": "https://avatars.githubusercontent.com/u/47017666?v=4",
"profile": "https://github.com/xijaja",
"contributions": [
"code"
]
},
{
"login": "almas1992",
"name": "ALMAS",
"avatar_url": "https://avatars.githubusercontent.com/u/9382335?v=4",
"profile": "https://www.almas.cc/",
"contributions": [
"code"
]
},
{
"login": "o8x",
"name": "Alex",
"avatar_url": "https://avatars.githubusercontent.com/u/20666153?v=4",
"profile": "https://stdout.com.cn/",
"contributions": [
"code"
]
},
{
"login": "arifali123",
"name": "Arif Ali",
"avatar_url": "https://avatars.githubusercontent.com/u/51419655?v=4",
"profile": "https://github.com/arifali123",
"contributions": [
"code"
]
},
{
"login": "hotafrika",
"name": "Artur Siarohau",
"avatar_url": "https://avatars.githubusercontent.com/u/18332839?v=4",
"profile": "https://github.com/hotafrika",
"contributions": [
"code"
]
},
{
"login": "binyamin",
"name": "Binyamin Aron Green",
"avatar_url": "https://avatars.githubusercontent.com/u/39805353?v=4",
"profile": "https://binyam.in/",
"contributions": [
"code"
]
},
{
"login": "bdwyertech",
"name": "Brian Dwyer",
"avatar_url": "https://avatars.githubusercontent.com/u/2973273?v=4",
"profile": "http://bdwyertech.net/",
"contributions": [
"code"
]
},
{
"login": "ckilb",
"name": "Christian Kilb",
"avatar_url": "https://avatars.githubusercontent.com/u/7283097?v=4",
"profile": "http://www.cilb.de/",
"contributions": [
"code"
]
},
{
"login": "edwargix",
"name": "David Florness",
"avatar_url": "https://avatars.githubusercontent.com/u/22877007?v=4",
"profile": "https://github.com/edwargix",
"contributions": [
"doc"
]
},
{
"login": "BuckeyeCoder",
"name": "David Walton",
"avatar_url": "https://avatars.githubusercontent.com/u/95933880?v=4",
"profile": "https://github.com/BuckeyeCoder",
"contributions": [
"code"
]
},
{
"login": "Debdut",
"name": "Debdut Karmakar",
"avatar_url": "https://avatars.githubusercontent.com/u/7561070?v=4",
"profile": "https://github.com/Debdut",
"contributions": [
"code"
]
},
{
"login": "gotid",
"name": "Dieter Zhu",
"avatar_url": "https://avatars.githubusercontent.com/u/4010854?v=4",
"profile": "https://github.com/gotid",
"contributions": [
"code"
]
},
{
"login": "Holmqvist1990",
"name": "Fredrik Holmqvist",
"avatar_url": "https://avatars.githubusercontent.com/u/22743750?v=4",
"profile": "https://fredrikholmqvist.com/",
"contributions": [
"code"
]
},
{
"login": "giopalma",
"name": "Giovanni Palma",
"avatar_url": "https://avatars.githubusercontent.com/u/33783684?v=4",
"profile": "https://github.com/giopalma",
"contributions": [
"code"
]
},
{
"login": "Nexus26404",
"name": "Hao",
"avatar_url": "https://avatars.githubusercontent.com/u/83110373?v=4",
"profile": "https://github.com/Nexus26404",
"contributions": [
"code"
]
},
{
"login": "i7tsov",
"name": "Igor Sementsov",
"avatar_url": "https://avatars.githubusercontent.com/u/44977153?v=4",
"profile": "https://github.com/i7tsov",
"contributions": [
"code"
]
},
{
"login": "derhasi",
"name": "Johannes Haseitl",
"avatar_url": "https://avatars.githubusercontent.com/u/118502?v=4",
"profile": "https://github.com/derhasi",
"contributions": [
"code"
]
},
{
"login": "joshbuddy",
"name": "Joshua Hull",
"avatar_url": "https://avatars.githubusercontent.com/u/8898?v=4",
"profile": "https://github.com/joshbuddy",
"contributions": [
"code"
]
},
{
"login": "joshm998",
"name": "Joshua Mangiola",
"avatar_url": "https://avatars.githubusercontent.com/u/1779737?v=4",
"profile": "https://github.com/joshm998",
"contributions": [
"doc"
]
},
{
"login": "prurigro",
"name": "Kevin MacMartin",
"avatar_url": "https://avatars.githubusercontent.com/u/1149238?v=4",
"profile": "https://github.com/prurigro",
"contributions": [
"code"
]
},
{
"login": "liang-li-dev",
"name": "Liang Li",
"avatar_url": "https://avatars.githubusercontent.com/u/112530363?v=4",
"profile": "https://github.com/liang-li-dev",
"contributions": [
"code"
]
},
{
"login": "marvinhosea",
"name": "Marvin Collins Hosea",
"avatar_url": "https://avatars.githubusercontent.com/u/7722584?v=4",
"profile": "https://appslab.co.ke/",
"contributions": [
"code"
]
},
{
"login": "mholt",
"name": "Matt Holt",
"avatar_url": "https://avatars.githubusercontent.com/u/1128849?v=4",
"profile": "https://matt.life/",
"contributions": [
"code"
]
},
{
"login": "Gurkengewuerz",
"name": "Niklas",
"avatar_url": "https://avatars.githubusercontent.com/u/10966337?v=4",
"profile": "https://github.com/Gurkengewuerz",
"contributions": [
"code"
]
},
{
"login": "Xhofe",
"name": "Andy Hsu",
"avatar_url": "https://avatars.githubusercontent.com/u/36558727?v=4",
"profile": "https://github.com/Xhofe",
"contributions": [
"code"
]
},
{
"login": "NullCode1337",
"name": "NullCode",
"avatar_url": "https://avatars.githubusercontent.com/u/70959549?v=4",
"profile": "https://github.com/NullCode1337",
"contributions": [
"code"
]
},
{
"login": "oSethoum",
"name": "Oussama Sethoum",
"avatar_url": "https://avatars.githubusercontent.com/u/88779394?v=4",
"profile": "https://github.com/oSethoum",
"contributions": [
"code"
]
},
{
"login": "ParkourLiu",
"name": "ParkourLiu",
"avatar_url": "https://avatars.githubusercontent.com/u/33681340?v=4",
"profile": "https://github.com/ParkourLiu",
"contributions": [
"code"
]
},
{
"login": "zllovesuki",
"name": "Rachel Chen",
"avatar_url": "https://avatars.githubusercontent.com/u/298453?v=4",
"profile": "https://github.com/zllovesuki",
"contributions": [
"code"
]
},
{
"login": "rnice01",
"name": "Rob Nice",
"avatar_url": "https://avatars.githubusercontent.com/u/11394384?v=4",
"profile": "https://github.com/rnice01",
"contributions": [
"code"
]
},
{
"login": "RyoTagami",
"name": "Ryo TAGAMI",
"avatar_url": "https://avatars.githubusercontent.com/u/9672589?v=4",
"profile": "https://github.com/RyoTagami",
"contributions": [
"code"
]
},
{
"login": "SamHennessy",
"name": "Sam Hennessy",
"avatar_url": "https://avatars.githubusercontent.com/u/119867?v=4",
"profile": "https://github.com/SamHennessy",
"contributions": [
"code"
]
},
{
"login": "AlbinoDrought",
"name": "Sean",
"avatar_url": "https://avatars.githubusercontent.com/u/852873?v=4",
"profile": "https://albinodrought.com/",
"contributions": [
"code"
]
},
{
"login": "sgosiaco",
"name": "Sean Gosiaco",
"avatar_url": "https://avatars.githubusercontent.com/u/212341?v=4",
"profile": "https://github.com/sgosiaco",
"contributions": [
"code"
]
},
{
"login": "SheetJSDev",
"name": "Eric P Sheets",
"avatar_url": "https://avatars.githubusercontent.com/u/6070939?v=4",
"profile": "https://sheetjs.com/",
"contributions": [
"code"
]
},
{
"login": "SupianIDz",
"name": "Supian M",
"avatar_url": "https://avatars.githubusercontent.com/u/37969970?v=4",
"profile": "https://www.octopy.dev/",
"contributions": [
"code"
]
},
{
"login": "Watson-Sei",
"name": "Watson-Sei",
"avatar_url": "https://avatars.githubusercontent.com/u/55475145?v=4",
"profile": "https://github.com/Watson-Sei",
"contributions": [
"code",
"doc"
]
},
{
"login": "shinshin86",
"name": "Yuki Shindo",
"avatar_url": "https://avatars.githubusercontent.com/u/8216064?v=4",
"profile": "https://shinshin86.com/",
"contributions": [
"code"
]
},
{
"login": "cuigege",
"name": "cuigege",
"avatar_url": "https://avatars.githubusercontent.com/u/26080122?v=4",
"profile": "https://github.com/cuigege",
"contributions": [
"code"
]
},
{
"login": "cybertramp",
"name": "cybertramp",
"avatar_url": "https://avatars.githubusercontent.com/u/30935096?v=4",
"profile": "https://cybertramp.net/",
"contributions": [
"code"
]
},
{
"login": "h8gi",
"name": "hiroki yagi",
"avatar_url": "https://avatars.githubusercontent.com/u/10811057?v=4",
"profile": "https://github.com/h8gi",
"contributions": [
"code"
]
},
{
"login": "imgbot[bot]",
"name": "imgbot[bot]",
"avatar_url": "https://avatars.githubusercontent.com/in/4706?v=4",
"profile": "https://github.com/apps/imgbot",
"contributions": [
"code"
]
},
{
"login": "tong3jie",
"name": "juju",
"avatar_url": "https://avatars.githubusercontent.com/u/14191774?v=4",
"profile": "https://github.com/tong3jie",
"contributions": [
"code"
]
},
{
"login": "meatherly",
"name": "Michael Eatherly",
"avatar_url": "https://avatars.githubusercontent.com/u/1327960?v=4",
"profile": "http://meatherly.github.io/",
"contributions": [
"code"
]
},
{
"login": "tk103331",
"name": "tk",
"avatar_url": "https://avatars.githubusercontent.com/u/4404609?v=4",
"profile": "https://github.com/tk103331",
"contributions": [
"code"
]
},
{
"login": "allcontributors[bot]",
"name": "allcontributors[bot]",
"avatar_url": "https://avatars.githubusercontent.com/in/23186?v=4",
"profile": "https://github.com/apps/allcontributors",
"contributions": [
"doc"
]
},
{
"login": "wandercn",
"name": "wander",
"avatar_url": "https://avatars.githubusercontent.com/u/77320953?v=4",
"profile": "https://www.ffactory.org/",
"contributions": [
"doc"
]
}
],
"contributorsPerLine": 8,

1
.eslintignore Normal file
View file

@ -0,0 +1 @@
runtime/assets/default.html

30
.eslintrc Normal file
View file

@ -0,0 +1,30 @@
{
"env": {
"browser": true,
"es6": true,
"node": true,
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 2016,
"sourceType": "module",
},
"rules": {
"indent": [
"error",
"tab"
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

View file

@ -7,7 +7,7 @@ body:
- type: markdown
attributes:
value: |
***Please note: No bug reports are currently being accepted for Wails v3***
***Please note: No new bug reports are being accepted for Wails v1***
Before submitting this issue, please do the following:
- Do a web search for your error. This usually leads to a much better understanding of the issue.
- Prove that the error is indeed a Wails bug and not an application bug, with a specific set of steps to reproduce.
@ -70,7 +70,7 @@ body:
validations:
required: false
- type: textarea
id: systemdetails
id: systemetails
attributes:
label: System Details
description: Please add the output of `wails doctor`.
@ -84,4 +84,4 @@ body:
description: Add any other context about the problem here.
placeholder: Add any other context about the problem here.
validations:
required: false
required: false

View file

@ -1,9 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Discord Chat
url: https://discord.gg/BrRSWTaxVK
about: Ask questions and discuss with other Wails users in real time.
- name: GitHub Community Discussions
url: https://github.com/wailsapp/wails/discussions
about: If your question is not a feature or a bug, please go to the discussion panel and retrieve if your question already exists before submitting.

View file

@ -1,44 +0,0 @@
# File path specific labels
v2-only:
- 'v2/**/*'
v3-alpha:
- 'v3/**/*'
windows:
- '**/*_windows.go'
- 'v2/internal/frontend/desktop/windows/**/*'
macos:
- '**/*_darwin.go'
- 'v2/internal/frontend/desktop/darwin/**/*'
linux:
- '**/*_linux.go'
- 'v2/internal/frontend/desktop/linux/**/*'
cli:
- 'v2/cmd/**/*'
- 'v3/cmd/**/*'
- '**/cli/**/*'
- '**/commands/**/*'
documentation:
- '**/*.md'
- 'docs/**/*'
- 'website/**/*'
- 'mkdocs-website/**/*'
templates:
- '**/templates/**/*'
- '**/template/**/*'
runtime:
- '**/runtime/**/*'
- 'v2/internal/runtime/**/*'
- 'v3/internal/runtime/**/*'
bindings:
- 'v2/internal/binding/**/*'
- 'v3/internal/generator/**/*'

View file

@ -1,144 +0,0 @@
# Version labels
v2-only:
- '\[v2\]'
- '\(v2\)'
- 'v2:'
- 'version 2'
- 'wails v2'
- 'using v2'
- 'master branch'
v3-alpha:
- '\[v3\]'
- '\(v3\)'
- 'v3:'
- '\[v3-alpha\]'
- '\(v3-alpha\)'
- 'version 3'
- 'wails v3'
- 'using v3'
- 'v3-alpha branch'
# Component labels
webview2:
- 'webview2'
- 'windows'
- 'microsoft edge'
- 'edge browser'
- 'IE'
- 'Explorer'
- 'browser crashes'
macos:
- 'macOS'
- 'mac OS'
- 'OS X'
- 'darwin'
- 'cocoa'
- 'Safari'
- 'Catalyst'
- 'Ventura'
- 'Sonoma'
- 'apple'
linux:
- 'linux'
- 'ubuntu'
- 'debian'
- 'fedora'
- 'gtk'
- 'webkitgtk'
- 'webkit2gtk'
- 'gnome'
- 'x11'
- 'wayland'
cli:
- 'cli'
- 'command line'
- 'wails doctor'
- 'wails init'
- 'wails build'
- 'wails dev'
- 'template'
- 'scaffolding'
# Type labels
bug:
- 'bug'
- 'crash'
- 'broken'
- 'failure'
- 'error'
- 'failed'
- 'panic'
- 'segfault'
- 'issue'
- 'not working'
- 'problem'
enhancement:
- 'feature'
- 'enhancement'
- 'request'
- 'add'
- 'new'
- 'improve'
- 'functionality'
- 'support for'
- 'please add'
- 'would be nice'
documentation:
- 'docs'
- 'documentation'
- 'readme'
- 'example'
- 'tutorial'
- 'guide'
- 'explanation'
- 'clarification'
- 'instructions'
security:
- 'security'
- 'vulnerability'
- 'exploit'
- 'hack'
- 'CVE'
- 'secure'
- 'encryption'
- 'hardening'
performance:
- 'performance'
- 'slow'
- 'speed'
- 'memory leak'
- 'cpu usage'
- 'high memory'
- 'lag'
- 'freeze'
- 'optimization'
# Priority labels
high-priority:
- 'urgent'
- 'critical'
- 'security'
- 'high priority'
- 'important'
- 'production'
- 'blocker'
- 'blocking'
question:
- 'how to'
- 'how do i'
- 'can I'
- 'is it possible'
- 'question'
- 'help me'
- 'need help'
- 'assistance'
- 'confused'

View file

@ -1,57 +0,0 @@
<!--
*********************************************************************
* PLEASE READ BEFORE SUBMITTING YOUR PR *
* YOUR PR MAY BE REJECTED IF IT DOES NOT FOLLOW THESE STEPS *
*********************************************************************
- *DO NOT* submit PRs for v3 alpha enhancements, unless you have opened a post on the discord channel.
All enhancements must be discussed first.
The feedback guide for v3 is here: https://v3alpha.wails.io/getting-started/feedback/
- Before submitting your PR, please ensure you have created and linked the PR to an issue.
- If a relevant issue already exists, please reference it in your PR by including `Fixes #<issue number>` in your PR description.
- Please fill in the checklists.
-->
# Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Fixes # (issue)
## Type of change
Please select the option that is relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] This change requires a documentation update
# How Has This Been Tested?
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using `wails doctor`.
- [ ] Windows
- [ ] macOS
- [ ] Linux
If you checked Linux, please specify the distro and version.
## Test Configuration
Please paste the output of `wails doctor`. If you are unable to run this command, please describe your environment in as much detail as possible.
# Checklist:
- [ ] I have updated `website/src/pages/changelog.mdx` with details of this PR
- [ ] My code follows the general coding style of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes

26
.github/stale.yml vendored
View file

@ -1,7 +1,7 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 45
daysUntilStale: 30
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 10
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
@ -9,28 +9,14 @@ exemptLabels:
- onhold
- inprogress
- "Selected For Development"
- bug
- enhancement
- v3-alpha
- high-priority
# Label to use when marking an issue as stale
staleLabel: "stale"
staleLabel: "Wont Fix"
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs within the next 10 days.
If this issue is still relevant, please add a comment to keep it open.
Thank you for your contributions.
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: >
This issue has been automatically closed due to lack of activity.
Please feel free to reopen it if it's still relevant.
closeComment: false
exemptMilestones: true
exemptAssignees: true
# Only mark issues (not PRs)
only: issues
# Exempt issues created before a certain date
exemptCreatedBefore: "2024-01-01T00:00:00Z"
# Starts checking issues only after the specified date
startDate: "2025-06-01T00:00:00Z"

View file

@ -1,33 +0,0 @@
name: Auto Label Issues
on:
issues:
types: [opened, edited, reopened]
pull_request:
types: [opened, edited, reopened, synchronize]
jobs:
auto-label:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Label issues and PRs by content
uses: github/issue-labeler@v3.4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/issue-labeler.yml
enable-versioned-regex: 0
include-title: 1
- name: Label issues and PRs by file paths
uses: actions/labeler@v4
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/file-labeler.yml
sync-labels: true

View file

@ -1,201 +0,0 @@
name: Build + Test v3
on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]
branches:
- v3-alpha
paths:
- 'v3/**'
pull_request_review:
types: [submitted]
branches:
- v3-alpha
jobs:
check_approval:
name: Check PR Approval
runs-on: ubuntu-latest
if: github.base_ref == 'v3-alpha'
outputs:
approved: ${{ steps.check.outputs.approved }}
steps:
- name: Check if PR is approved
id: check
run: |
if [[ "${{ github.event.review.state }}" == "approved" || "${{ github.event.pull_request.approved }}" == "true" ]]; then
echo "approved=true" >> $GITHUB_OUTPUT
else
echo "approved=false" >> $GITHUB_OUTPUT
fi
test_go:
name: Run Go Tests v3
needs: check_approval
runs-on: ${{ matrix.os }}
if: github.base_ref == 'v3-alpha'
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
go-version: [1.24]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install linux dependencies
uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-latest'
with:
packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config xvfb x11-xserver-utils at-spi2-core xdg-desktop-portal-gtk
version: 1.0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
cache-dependency-path: "v3/go.sum"
- name: Install Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Examples
working-directory: v3
run: task test:examples
- name: Run tests (mac)
if: matrix.os == 'macos-latest'
env:
CGO_LDFLAGS: -framework UniformTypeIdentifiers -mmacosx-version-min=10.13
working-directory: v3
run: go test -v ./...
- name: Run tests (windows)
if: matrix.os == 'windows-latest'
working-directory: v3
run: go test -v ./...
- name: Run tests (ubuntu)
if: matrix.os == 'ubuntu-latest'
working-directory: v3
run: >
xvfb-run --auto-servernum
sh -c '
dbus-update-activation-environment --systemd --all &&
go test -v ./...
'
- name: Typecheck binding generator output
working-directory: v3
run: task generator:test:check
test_js:
name: Run JS Tests
needs: check_approval
runs-on: ubuntu-latest
if: github.base_ref == 'v3-alpha'
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install dependencies
run: npm install
working-directory: v2/internal/frontend/runtime
- name: Run tests
run: npm test
working-directory: v2/internal/frontend/runtime
test_templates:
name: Test Templates
needs: test_go
runs-on: ${{ matrix.os }}
if: github.base_ref == 'v3-alpha'
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
template:
- svelte
- svelte-ts
- vue
- vue-ts
- react
- react-ts
- preact
- preact-ts
- lit
- lit-ts
- vanilla
- vanilla-ts
go-version: [1.24]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install linux dependencies
uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-latest'
with:
packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config
version: 1.0
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: ${{ matrix.go-version }}
cache-dependency-path: "v3/go.sum"
- name: Install Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Build Wails3 CLI
working-directory: v3
run: |
task install
wails3 doctor
- name: Generate template '${{ matrix.template }}'
run: |
mkdir -p ./test-${{ matrix.template }}
cd ./test-${{ matrix.template }}
wails3 init -n ${{ matrix.template }} -t ${{ matrix.template }}
cd ${{ matrix.template }}
wails3 build
build_results:
if: ${{ always() }}
runs-on: ubuntu-latest
name: v3 Build Results
needs: [test_go, test_js, test_templates]
steps:
- run: |
go_result="${{ needs.test_go.result }}"
js_result="${{ needs.test_js.result }}"
templates_result="${{ needs.test_templates.result }}"
if [[ $go_result == "success" || $go_result == "skipped" ]] && \
[[ $js_result == "success" || $js_result == "skipped" ]] && \
[[ $templates_result == "success" || $templates_result == "skipped" ]]; then
echo "All required jobs succeeded or were skipped"
exit 0
else
echo "One or more required jobs failed"
exit 1
fi

View file

@ -1,8 +1,8 @@
name: Build + Test v2
name: Build + Test
on:
push:
branches: [release/*, master, bugfix/*]
branches: [release/*, master]
workflow_dispatch:
jobs:
@ -12,30 +12,21 @@ jobs:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-24.04, windows-latest, macos-latest]
go-version: ['1.22']
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: [1.18, 1.19]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-22.04'
with:
packages: libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
version: 1.0
- uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-24.04'
with:
packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config libegl1
version: 1.0
- name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
- name: Setup Go
uses: actions/setup-go@v4
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
cache-dependency-path: ./v2/go.sum
- name: Run tests (mac)
if: matrix.os == 'macos-latest'
@ -45,26 +36,21 @@ jobs:
run: go test -v ./...
- name: Run tests (!mac)
if: matrix.os != 'macos-latest' && matrix.os != 'ubuntu-24.04'
if: matrix.os != 'macos-latest'
working-directory: ./v2
run: go test -v ./...
- name: Run tests (Ubuntu 24.04)
if: matrix.os == 'ubuntu-24.04'
working-directory: ./v2
run: go test -v -tags webkit2_41 ./...
test_js:
name: Run JS Tests
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x]
node-version: [16.x]
steps:
- name: Checkout code
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
@ -86,7 +72,7 @@ jobs:
strategy:
fail-fast: true
matrix:
os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04]
os: [ubuntu-latest, windows-latest, macos-latest]
template:
[
svelte,
@ -103,16 +89,15 @@ jobs:
vanilla-ts,
plain,
]
go-version: ['1.22']
go-version: [1.18, 1.19]
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Go
uses: actions/setup-go@v5
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
cache-dependency-path: ./v2/go.sum
- name: Build Wails CLI
run: |
@ -120,41 +105,14 @@ jobs:
go install
wails -help
- uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-22.04'
with:
packages: libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
version: 1.0
- name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
# - name: Install linux dependencies ( 22.04 )
# if: matrix.os == 'ubuntu-22.04'
# run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
- uses: awalsh128/cache-apt-pkgs-action@latest
if: matrix.os == 'ubuntu-24.04'
with:
packages: libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config libegl1
version: 1.0
# - name: Install linux dependencies ( 24.04 )
# if: matrix.os == 'ubuntu-24.04'
# run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config
- name: Generate & Build template '${{ matrix.template }}'
if: matrix.os != 'ubuntu-24.04'
- name: Generate template '${{ matrix.template }}'
run: |
mkdir -p ./test-${{ matrix.template }}
cd ./test-${{ matrix.template }}
wails init -n ${{ matrix.template }} -t ${{ matrix.template }} -ci
cd ${{ matrix.template }}
wails build -v 2
- name: Generate & Build template '${{ matrix.template }}' (ubuntu-24.04)
if: matrix.os == 'ubuntu-24.04'
run: |
mkdir -p ./test-${{ matrix.template }}
cd ./test-${{ matrix.template }}
wails init -n ${{ matrix.template }} -t ${{ matrix.template }} -ci
cd ${{ matrix.template }}
wails build -v 2 -tags webkit2_41

View file

@ -1,423 +0,0 @@
name: Build Cross-Compiler Image
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch containing Dockerfile'
required: true
default: 'v3-alpha'
sdk_version:
description: 'macOS SDK version'
required: true
default: '14.5'
zig_version:
description: 'Zig version'
required: true
default: '0.14.0'
image_version:
description: 'Image version tag'
required: true
default: 'latest'
skip_tests:
description: 'Skip cross-compilation tests'
required: false
default: 'false'
type: boolean
push:
branches:
- v3-alpha
paths:
- 'v3/internal/commands/build_assets/docker/Dockerfile.cross'
env:
REGISTRY: ghcr.io
IMAGE_NAME: wailsapp/wails-cross
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
outputs:
image_tag: ${{ steps.vars.outputs.image_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.branch || github.ref }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Set build variables
id: vars
run: |
echo "sdk_version=${{ inputs.sdk_version || '14.5' }}" >> $GITHUB_OUTPUT
echo "zig_version=${{ inputs.zig_version || '0.14.0' }}" >> $GITHUB_OUTPUT
echo "image_version=${{ inputs.image_version || 'latest' }}" >> $GITHUB_OUTPUT
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest
type=raw,value=${{ steps.vars.outputs.image_version }}
type=raw,value=sdk-${{ steps.vars.outputs.sdk_version }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: v3/internal/commands/build_assets/docker
file: v3/internal/commands/build_assets/docker/Dockerfile.cross
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: |
${{ steps.meta.outputs.labels }}
io.wails.zig.version=${{ steps.vars.outputs.zig_version }}
io.wails.sdk.version=${{ steps.vars.outputs.sdk_version }}
build-args: |
ZIG_VERSION=${{ steps.vars.outputs.zig_version }}
MACOS_SDK_VERSION=${{ steps.vars.outputs.sdk_version }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Test cross-compilation for all platforms
test-cross-compile:
needs: build
if: ${{ inputs.skip_tests != 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Darwin targets (Zig + macOS SDK) - no platform emulation needed
- os: darwin
arch: arm64
platform: ""
expected_file: "Mach-O 64-bit.*arm64"
- os: darwin
arch: amd64
platform: ""
expected_file: "Mach-O 64-bit.*x86_64"
# Linux targets (GCC) - need platform to match architecture
- os: linux
arch: amd64
platform: "linux/amd64"
expected_file: "ELF 64-bit LSB.*x86-64"
- os: linux
arch: arm64
platform: "linux/arm64"
expected_file: "ELF 64-bit LSB.*ARM aarch64"
# Windows targets (Zig + mingw) - no platform emulation needed
- os: windows
arch: amd64
platform: ""
expected_file: "PE32\\+ executable.*x86-64"
- os: windows
arch: arm64
platform: ""
expected_file: "PE32\\+ executable.*Aarch64"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.branch || github.ref }}
- name: Set up QEMU
if: matrix.platform != ''
uses: docker/setup-qemu-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create test CGO project
run: |
mkdir -p test-project
cd test-project
# Create a minimal CGO test program
cat > main.go << 'EOF'
package main
/*
#include <stdlib.h>
int add(int a, int b) {
return a + b;
}
*/
import "C"
import "fmt"
func main() {
result := C.add(1, 2)
fmt.Printf("CGO test: 1 + 2 = %d\n", result)
}
EOF
cat > go.mod << 'EOF'
module test-cgo
go 1.21
EOF
- name: Build ${{ matrix.os }}/${{ matrix.arch }} (CGO)
run: |
cd test-project
PLATFORM_FLAG=""
if [ -n "${{ matrix.platform }}" ]; then
PLATFORM_FLAG="--platform ${{ matrix.platform }}"
fi
docker run --rm $PLATFORM_FLAG \
-v "$(pwd):/app" \
-e APP_NAME="test-cgo" \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image_tag || 'latest' }} \
${{ matrix.os }} ${{ matrix.arch }}
- name: Verify binary format
run: |
cd test-project/bin
ls -la
# Find the built binary
if [ "${{ matrix.os }}" = "windows" ]; then
BINARY=$(ls test-cgo-${{ matrix.os }}-${{ matrix.arch }}.exe 2>/dev/null || ls *.exe | head -1)
else
BINARY=$(ls test-cgo-${{ matrix.os }}-${{ matrix.arch }} 2>/dev/null || ls test-cgo* | grep -v '.exe' | head -1)
fi
echo "Binary: $BINARY"
FILE_OUTPUT=$(file "$BINARY")
echo "File output: $FILE_OUTPUT"
# Verify the binary format matches expected
if echo "$FILE_OUTPUT" | grep -qE "${{ matrix.expected_file }}"; then
echo "✅ Binary format verified: ${{ matrix.os }}/${{ matrix.arch }}"
else
echo "❌ Binary format mismatch!"
echo "Expected pattern: ${{ matrix.expected_file }}"
echo "Got: $FILE_OUTPUT"
exit 1
fi
- name: Check library dependencies (Linux only)
if: matrix.os == 'linux'
run: |
cd test-project/bin
BINARY=$(ls test-cgo-${{ matrix.os }}-${{ matrix.arch }} 2>/dev/null || ls test-cgo* | grep -v '.exe' | head -1)
echo "## Library Dependencies for $BINARY"
echo ""
# Use readelf to show dynamic dependencies
echo "### NEEDED libraries:"
readelf -d "$BINARY" | grep NEEDED || echo "No dynamic dependencies (statically linked)"
# Verify expected libraries are linked
echo ""
echo "### Verifying required libraries..."
NEEDED=$(readelf -d "$BINARY" | grep NEEDED)
MISSING=""
for lib in libwebkit2gtk-4.1.so libgtk-3.so libglib-2.0.so libc.so; do
if echo "$NEEDED" | grep -q "$lib"; then
echo "✅ $lib"
else
echo "❌ $lib MISSING"
MISSING="$MISSING $lib"
fi
done
if [ -n "$MISSING" ]; then
echo ""
echo "ERROR: Missing required libraries:$MISSING"
exit 1
fi
# Test non-CGO builds (pure Go cross-compilation)
test-non-cgo:
needs: build
if: ${{ inputs.skip_tests != 'true' }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- os: darwin
arch: arm64
expected_file: "Mach-O 64-bit.*arm64"
- os: darwin
arch: amd64
expected_file: "Mach-O 64-bit.*x86_64"
- os: linux
arch: amd64
expected_file: "ELF 64-bit LSB"
- os: linux
arch: arm64
expected_file: "ELF 64-bit LSB.*ARM aarch64"
- os: windows
arch: amd64
expected_file: "PE32\\+ executable.*x86-64"
- os: windows
arch: arm64
expected_file: "PE32\\+ executable.*Aarch64"
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ inputs.branch || github.ref }}
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Create test non-CGO project
run: |
mkdir -p test-project
cd test-project
# Create a pure Go test program (no CGO)
cat > main.go << 'EOF'
package main
import "fmt"
func main() {
fmt.Println("Pure Go cross-compilation test")
}
EOF
cat > go.mod << 'EOF'
module test-pure-go
go 1.21
EOF
- name: Build ${{ matrix.os }}/${{ matrix.arch }} (non-CGO)
run: |
cd test-project
# For non-CGO, we can use any platform since Go handles cross-compilation
# We set CGO_ENABLED=0 to ensure pure Go build
docker run --rm \
-v "$(pwd):/app" \
-e APP_NAME="test-pure-go" \
-e CGO_ENABLED=0 \
--entrypoint /bin/sh \
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image_tag || 'latest' }} \
-c "GOOS=${{ matrix.os }} GOARCH=${{ matrix.arch }} go build -o bin/test-pure-go-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.os == 'windows' && '.exe' || '' }} ."
- name: Verify binary format
run: |
cd test-project/bin
ls -la
# Find the built binary
if [ "${{ matrix.os }}" = "windows" ]; then
BINARY="test-pure-go-${{ matrix.os }}-${{ matrix.arch }}.exe"
else
BINARY="test-pure-go-${{ matrix.os }}-${{ matrix.arch }}"
fi
echo "Binary: $BINARY"
FILE_OUTPUT=$(file "$BINARY")
echo "File output: $FILE_OUTPUT"
# Verify the binary format matches expected
if echo "$FILE_OUTPUT" | grep -qE "${{ matrix.expected_file }}"; then
echo "✅ Binary format verified: ${{ matrix.os }}/${{ matrix.arch }} (non-CGO)"
else
echo "❌ Binary format mismatch!"
echo "Expected pattern: ${{ matrix.expected_file }}"
echo "Got: $FILE_OUTPUT"
exit 1
fi
- name: Check library dependencies (Linux only)
if: matrix.os == 'linux'
run: |
cd test-project/bin
BINARY="test-pure-go-${{ matrix.os }}-${{ matrix.arch }}"
echo "## Library Dependencies for $BINARY (non-CGO)"
echo ""
# Non-CGO builds should have minimal dependencies (just libc or statically linked)
echo "### NEEDED libraries:"
readelf -d "$BINARY" | grep NEEDED || echo "No dynamic dependencies (statically linked)"
# Verify NO GTK/WebKit libraries (since CGO is disabled)
NEEDED=$(readelf -d "$BINARY" | grep NEEDED || true)
if echo "$NEEDED" | grep -q "libwebkit\|libgtk"; then
echo "❌ ERROR: Non-CGO binary should not link to GTK/WebKit!"
exit 1
else
echo "✅ Confirmed: No GTK/WebKit dependencies (expected for non-CGO)"
fi
# Summary job
test-summary:
needs: [build, test-cross-compile, test-non-cgo]
if: always() && inputs.skip_tests != 'true'
runs-on: ubuntu-latest
steps:
- name: Check test results
run: |
echo "## Cross-Compilation Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.test-cross-compile.result }}" = "success" ]; then
echo "✅ **CGO Tests**: All passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **CGO Tests**: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.test-non-cgo.result }}" = "success" ]; then
echo "✅ **Non-CGO Tests**: All passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Non-CGO Tests**: Failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Tested Platforms" >> $GITHUB_STEP_SUMMARY
echo "| Platform | Architecture | CGO | Non-CGO |" >> $GITHUB_STEP_SUMMARY
echo "|----------|-------------|-----|---------|" >> $GITHUB_STEP_SUMMARY
echo "| Darwin | arm64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
echo "| Darwin | amd64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
echo "| Linux | arm64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
echo "| Linux | amd64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
echo "| Windows | arm64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
echo "| Windows | amd64 | ✅ | ✅ |" >> $GITHUB_STEP_SUMMARY
# Fail if any test failed
if [ "${{ needs.test-cross-compile.result }}" != "success" ] || [ "${{ needs.test-non-cgo.result }}" != "success" ]; then
echo ""
echo "❌ Some tests failed. Check the individual job logs for details."
exit 1
fi

View file

@ -1,216 +0,0 @@
name: Changelog Validation (v3)
on:
pull_request:
branches: [ v3-alpha ]
paths:
- 'docs/src/content/docs/changelog.mdx'
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to validate'
required: true
type: string
jobs:
validate:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
actions: write
steps:
- name: Checkout PR code
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || format('refs/pull/{0}/head', github.event.inputs.pr_number) }}
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN || github.token }}
- name: Get REAL validation script from v3-alpha
run: |
echo "Fetching the REAL validation script from v3-alpha branch..."
git fetch origin v3-alpha
git checkout origin/v3-alpha -- v3/scripts/validate-changelog.go
echo "Validation script fetched successfully:"
ls -la v3/scripts/
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.23'
- name: Get PR information
id: pr_info
run: |
if [ "${{ github.event_name }}" = "pull_request" ]; then
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
echo "base_ref=${{ github.event.pull_request.base.ref }}" >> $GITHUB_OUTPUT
else
echo "pr_number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
echo "base_ref=v3-alpha" >> $GITHUB_OUTPUT
fi
- name: Check changelog modifications
id: changelog_check
run: |
echo "Checking PR #${{ steps.pr_info.outputs.pr_number }} for changelog changes"
git fetch origin ${{ steps.pr_info.outputs.base_ref }}
if git diff --name-only origin/${{ steps.pr_info.outputs.base_ref }}..HEAD | grep -q "docs/src/content/docs/changelog.mdx"; then
echo "changelog_modified=true" >> $GITHUB_OUTPUT
echo "✅ Changelog was modified in this PR"
else
echo "changelog_modified=false" >> $GITHUB_OUTPUT
echo " Changelog was not modified - skipping validation"
fi
- name: Get changelog diff
id: get_diff
if: steps.changelog_check.outputs.changelog_modified == 'true'
run: |
echo "Getting diff for changelog changes..."
git diff origin/${{ steps.pr_info.outputs.base_ref }}..HEAD docs/src/content/docs/changelog.mdx | grep "^+" | grep -v "^+++" | sed 's/^+//' > /tmp/pr_added_lines.txt
echo "Lines added in this PR:"
cat /tmp/pr_added_lines.txt
echo "Total lines added: $(wc -l < /tmp/pr_added_lines.txt)"
- name: Validate changelog
id: validate
if: steps.changelog_check.outputs.changelog_modified == 'true'
run: |
echo "Running changelog validation..."
cd v3/scripts
OUTPUT=$(go run validate-changelog.go ../../docs/src/content/docs/changelog.mdx /tmp/pr_added_lines.txt 2>&1)
echo "$OUTPUT"
RESULT=$(echo "$OUTPUT" | grep "VALIDATION_RESULT=" | cut -d'=' -f2)
echo "result=$RESULT" >> $GITHUB_OUTPUT
- name: Commit fixes
id: commit_fixes
if: steps.validate.outputs.result == 'fixed'
run: |
echo "Committing automatic fixes..."
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
# Check only the changelog file for changes
if git diff --quiet docs/src/content/docs/changelog.mdx; then
echo "No changes to commit"
echo "committed=false" >> $GITHUB_OUTPUT
else
# Ensure validation script doesn't get committed
echo "v3/scripts/validate-changelog.go" >> .git/info/exclude
# Get the correct branch name to push to
REPO_OWNER="wailsapp" # Always wailsapp for this repo
if [ "${{ github.event_name }}" = "pull_request" ]; then
BRANCH_NAME="${{ github.event.pull_request.head.ref }}"
else
# For manual workflow dispatch, get PR info
PR_INFO=$(gh pr view ${{ steps.pr_info.outputs.pr_number }} --json headRefName,headRepository)
BRANCH_NAME=$(echo "$PR_INFO" | jq -r '.headRefName')
HEAD_REPO=$(echo "$PR_INFO" | jq -r '.headRepository.name')
echo "🔍 PR source branch: $BRANCH_NAME"
echo "🔍 Head repository: $HEAD_REPO"
# Don't push if this is from a fork or if branch is v3-alpha (main branch)
if [ "$HEAD_REPO" != "wails" ] || [ "$BRANCH_NAME" = "v3-alpha" ]; then
echo "⚠️ Cannot push - either fork or direct v3-alpha branch. Manual fix required."
echo "committed=false" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "Pushing to branch: $BRANCH_NAME in repo: $REPO_OWNER"
# Only commit the changelog changes, not the validation script
git add docs/src/content/docs/changelog.mdx
git commit -m "🤖 Fix changelog: move entries to Unreleased section"
# Only push if running on the main wailsapp repository
if [ "${{ github.repository }}" = "wailsapp/wails" ]; then
# Pull latest changes and rebase our commit
git fetch origin $BRANCH_NAME
git rebase origin/$BRANCH_NAME
git push origin HEAD:$BRANCH_NAME
else
echo "⚠️ Running on fork (${{ github.repository }}). Skipping push - manual fix required."
echo "committed=false" >> $GITHUB_OUTPUT
exit 0
fi
echo "committed=true" >> $GITHUB_OUTPUT
echo "✅ Changes committed and pushed"
fi
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get PR author for tagging
id: pr_author
if: steps.validate.outputs.result && github.event.inputs.pr_number
run: |
PR_AUTHOR=$(gh pr view ${{ steps.pr_info.outputs.pr_number }} --json author --jq '.author.login')
echo "author=$PR_AUTHOR" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Comment on PR
if: steps.validate.outputs.result && github.event.inputs.pr_number
uses: actions/github-script@v7
with:
script: |
const result = '${{ steps.validate.outputs.result }}';
const committed = '${{ steps.commit_fixes.outputs.committed }}';
const author = '${{ steps.pr_author.outputs.author }}';
let message;
if (result === 'success') {
message = '## ✅ Changelog Validation Passed\n\nNo misplaced changelog entries detected.';
} else if (result === 'fixed' && committed === 'true') {
message = '## 🔧 Changelog Updated\n\nMisplaced entries were automatically moved to the `[Unreleased]` section. The changes have been committed to this PR.';
} else if (result === 'fixed' || result === 'cannot_fix' || result === 'error') {
// Read the fixed changelog content
const fs = require('fs');
let fixedContent = '';
try {
fixedContent = fs.readFileSync('docs/src/content/docs/changelog.mdx', 'utf8');
} catch (error) {
fixedContent = 'Error reading fixed changelog content';
}
message = '## ⚠️ Changelog Validation Issue\\n\\n' +
'@' + author + ' Your PR contains changelog entries that were added to already-released versions. These need to be moved to the `[Unreleased]` section.\\n\\n' +
(committed === 'true' ?
'✅ **Auto-fix applied**: The changes have been automatically committed to this PR.' :
'❌ **Manual fix required**: Please apply the changes shown below manually.') + '\\n\\n' +
'<details>\\n' +
'<summary>📝 Click to see the corrected changelog content</summary>\\n\\n' +
'```mdx\\n' +
fixedContent +
'\\n```\\n\\n' +
'</details>\\n\\n' +
'**What happened?** \\n' +
'The validation script detected that you added changelog entries to a version section that has already been released (like `v3.0.0-alpha.10`). All new entries should go in the `[Unreleased]` section under the appropriate category (`### Added`, `### Fixed`, etc.).\\n\\n' +
(committed !== 'true' ? '**Action needed:** Please copy the corrected content from above and replace your changelog file.' : '');
}
if (message) {
await github.rest.issues.createComment({
issue_number: ${{ steps.pr_info.outputs.pr_number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: message
});
}
- name: Fail if validation failed
if: steps.validate.outputs.result == 'cannot_fix' || steps.validate.outputs.result == 'error'
run: |
echo "❌ Changelog validation failed"
exit 1

View file

@ -1,44 +0,0 @@
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize, ready_for_review, reopened]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
plugin_marketplaces: 'https://github.com/anthropics/claude-code.git'
plugins: 'code-review@claude-code-plugins'
prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}'
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options

View file

@ -1,50 +0,0 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'

View file

@ -16,7 +16,7 @@ jobs:
- name: Set Node
uses: actions/setup-node@v2
with:
node-version: 20.x
node-version: 16.x
- name: Update Sponsors
run: cd scripts/sponsors && chmod 755 ./generate-sponsor-image.sh && ./generate-sponsor-image.sh
@ -25,16 +25,11 @@ jobs:
SPONSORKIT_GITHUB_LOGIN: wailsapp
- name: Create Pull Request
uses: peter-evans/create-pull-request@v6
uses: peter-evans/create-pull-request@v4
with:
commit-message: "chore: update sponsors.svg"
add-paths: "website/static/img/sponsors.svg"
title: "chore: update sponsors.svg"
body: |
Auto-generated by the sponsor image workflow
[skip ci] [skip actions]
title: Update Sponsor Image
body: Generated new image
branch: update-sponsors
base: master
delete-branch: true
draft: false

View file

@ -1,77 +0,0 @@
name: Issue Triage Automation
on:
issues:
types: [opened]
jobs:
triage:
runs-on: ubuntu-latest
permissions:
issues: write
contents: read
steps:
# Request more info for unclear bug reports
- name: Request more info
uses: actions/github-script@v6
if: |
contains(github.event.issue.labels.*.name, 'bug') &&
!contains(github.event.issue.body, 'wails doctor') &&
!contains(github.event.issue.body, 'reproduction')
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: `👋 Thanks for reporting this issue! To help us investigate, could you please:
1. Add the output of \`wails doctor\` if not already included
2. Provide clear steps to reproduce the issue
3. If possible, create a minimal reproduction of the issue
This will help us resolve your issue much faster. Thank you!`
});
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['awaiting feedback']
});
# Prioritize security issues
- name: Prioritize security issues
uses: actions/github-script@v6
if: contains(github.event.issue.labels.*.name, 'security')
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['high-priority']
});
# Tag version-specific issues for project boards
- name: Add to v2 project
uses: actions/github-script@v6
if: |
contains(github.event.issue.labels.*.name, 'v2-only') &&
!contains(github.event.issue.labels.*.name, 'v3-alpha')
with:
script: |
// Replace PROJECT_ID with your actual GitHub project ID
// This is a placeholder as the actual implementation would require
// GraphQL API calls to add to a project board
console.log('Would add to v2 project board');
# Tag version-specific issues for project boards
- name: Add to v3 project
uses: actions/github-script@v6
if: contains(github.event.issue.labels.*.name, 'v3-alpha')
with:
script: |
// Replace PROJECT_ID with your actual GitHub project ID
// This is a placeholder as the actual implementation would require
// GraphQL API calls to add to a project board
console.log('Would add to v3 project board');

15
.github/workflows/label-sponsors.yml vendored Normal file
View file

@ -0,0 +1,15 @@
name: Label sponsors
on:
pull_request:
types: [ opened ]
issues:
types: [ opened ]
jobs:
build:
name: is-sponsor-label
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
steps:
- uses: JasonEtco/is-sponsor-label-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -1,210 +0,0 @@
name: Nightly Release v3-alpha
on:
schedule:
- cron: '0 2 * * *' # 2 AM UTC daily
workflow_dispatch:
inputs:
force_release:
description: 'Force release even if no changes detected'
required: false
default: false
type: boolean
dry_run:
description: 'Run in dry-run mode (no actual release)'
required: false
default: true
type: boolean
jobs:
nightly-release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
actions: write
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: v3-alpha
fetch-depth: 0
token: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.24'
cache: true
cache-dependency-path: 'v3/go.sum'
- name: Install Task
uses: arduino/setup-task@v2
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Configure git to use the token for authentication
git config --global url."https://x-access-token:${{ secrets.WAILS_REPO_TOKEN || github.token }}@github.com/".insteadOf "https://github.com/"
- name: Check for existing release tag
id: check_tag
run: |
if git describe --tags --exact-match HEAD 2>/dev/null; then
echo "has_tag=true" >> $GITHUB_OUTPUT
echo "tag=$(git describe --tags --exact-match HEAD)" >> $GITHUB_OUTPUT
else
echo "has_tag=false" >> $GITHUB_OUTPUT
echo "tag=" >> $GITHUB_OUTPUT
fi
- name: Check for unreleased changelog content
id: changelog_check
run: |
echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..."
# Run the release script in check mode to see if there's content
cd v3/tasks/release
# Use the release script itself to check for content
if go run release.go --check-only 2>/dev/null; then
echo "has_unreleased_content=true" >> $GITHUB_OUTPUT
echo "✅ Found unreleased changelog content"
else
echo "has_unreleased_content=false" >> $GITHUB_OUTPUT
echo " No unreleased changelog content found"
fi
- name: Quick change detection and early exit
id: quick_check
run: |
echo "🔍 Quick check for changes to determine if we should continue..."
# First check if we have unreleased changelog content
if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then
echo "✅ Found unreleased changelog content, proceeding with release"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
echo "reason=Found unreleased changelog content" >> $GITHUB_OUTPUT
exit 0
fi
# If no unreleased changelog content, check for git changes as fallback
echo "No unreleased changelog content found, checking for git changes..."
# Check if current commit has a release tag
if git describe --tags --exact-match HEAD 2>/dev/null; then
CURRENT_TAG=$(git describe --tags --exact-match HEAD)
echo "Current commit has release tag: $CURRENT_TAG"
# For tagged commits, check if there are changes since the tag
COMMIT_COUNT=$(git rev-list ${CURRENT_TAG}..HEAD --count)
if [ "$COMMIT_COUNT" -eq 0 ]; then
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "should_continue=false" >> $GITHUB_OUTPUT
echo "reason=No changes since existing tag $CURRENT_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT
else
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
fi
else
# No current tag, check against latest release
LATEST_TAG=$(git tag --list "v3.0.0-alpha.*" | sort -V | tail -1)
if [ -z "$LATEST_TAG" ]; then
echo "No previous release found, proceeding with release"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
else
COMMIT_COUNT=$(git rev-list ${LATEST_TAG}..HEAD --count)
if [ "$COMMIT_COUNT" -gt 0 ]; then
echo "Found $COMMIT_COUNT commits since $LATEST_TAG"
echo "has_changes=true" >> $GITHUB_OUTPUT
echo "should_continue=true" >> $GITHUB_OUTPUT
else
echo "has_changes=false" >> $GITHUB_OUTPUT
echo "should_continue=false" >> $GITHUB_OUTPUT
echo "reason=No changes since latest release $LATEST_TAG and no unreleased changelog content" >> $GITHUB_OUTPUT
fi
fi
fi
- name: Early exit - No changes detected
if: |
steps.quick_check.outputs.should_continue == 'false' &&
github.event.inputs.force_release != 'true'
run: |
echo "🛑 EARLY EXIT: ${{ steps.quick_check.outputs.reason }}"
echo ""
echo " No changes detected since last release and force_release is not enabled."
echo " Workflow will exit early to save resources."
echo ""
echo " To force a release anyway, run this workflow with 'force_release=true'"
echo ""
echo "## 🛑 Early Exit Summary" >> $GITHUB_STEP_SUMMARY
echo "**Reason:** ${{ steps.quick_check.outputs.reason }}" >> $GITHUB_STEP_SUMMARY
echo "**Action:** Workflow exited early to save resources" >> $GITHUB_STEP_SUMMARY
echo "**Force Release:** Set 'force_release=true' to override this behavior" >> $GITHUB_STEP_SUMMARY
exit 0
- name: Continue with release process
if: |
steps.quick_check.outputs.should_continue == 'true' ||
github.event.inputs.force_release == 'true'
run: |
echo "✅ Proceeding with release process..."
if [ "${{ github.event.inputs.force_release }}" == "true" ]; then
echo "🔨 FORCE RELEASE: Overriding change detection"
fi
- name: Run release script
id: release
if: |
steps.quick_check.outputs.should_continue == 'true' ||
github.event.inputs.force_release == 'true'
env:
WAILS_REPO_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
GITHUB_TOKEN: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
run: |
cd v3/tasks/release
ARGS=()
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
ARGS+=(--dry-run)
fi
go run release.go "${ARGS[@]}"
- name: Summary
if: always()
run: |
if [ "${{ github.event.inputs.dry_run }}" == "true" ]; then
echo "## 🧪 DRY RUN Release Summary" >> $GITHUB_STEP_SUMMARY
else
echo "## 🚀 Nightly Release Summary" >> $GITHUB_STEP_SUMMARY
fi
echo "================================" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.release.outputs.release_version }}" ]; then
echo "- **Version:** ${{ steps.release.outputs.release_version }}" >> $GITHUB_STEP_SUMMARY
echo "- **Tag:** ${{ steps.release.outputs.release_tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Status:** ${{ steps.release.outcome == 'success' && '✅ Success' || '⚠️ Failed' }}" >> $GITHUB_STEP_SUMMARY
echo "- **Mode:** ${{ steps.release.outputs.release_dry_run == 'true' && '🧪 Dry Run' || '🚀 Live release' }}" >> $GITHUB_STEP_SUMMARY
if [ -n "${{ steps.release.outputs.release_url }}" ]; then
echo "- **Release URL:** ${{ steps.release.outputs.release_url }}" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "### Changelog" >> $GITHUB_STEP_SUMMARY
if [ "${{ steps.changelog_check.outputs.has_unreleased_content }}" == "true" ]; then
echo "✅ Unreleased changelog processed and reset." >> $GITHUB_STEP_SUMMARY
else
echo " No unreleased changelog content detected." >> $GITHUB_STEP_SUMMARY
fi
else
echo "- Release script did not run (skipped or failed before execution)." >> $GITHUB_STEP_SUMMARY
fi

View file

@ -1,104 +0,0 @@
# Updated to ensure "Run Go Tests" runs for pull requests as expected.
# Key fix: the test_go job previously required github.event.review.state == 'approved'
# which only exists on pull_request_review events. That prevented the job from
# running for regular pull_request events (opened / synchronize / reopened).
# New logic: run tests for pull_request events, and also allow running when a
# pull_request_review is submitted with state == 'approved'.
on:
pull_request:
types: [opened, synchronize, reopened]
branches:
- master
pull_request_review:
types: [submitted]
branches:
- master
workflow_dispatch: {}
name: PR Checks (master)
jobs:
check_docs:
name: Check Docs
if: ${{ github.repository == 'wailsapp/wails' && github.base_ref == 'master' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Verify Changed files
uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1
id: verify-changed-files
with:
files: |
website/**/*.mdx
website/**/*.md
- name: Run step only when files change.
if: steps.verify-changed-files.outputs.files_changed != 'true'
run: |
echo "::warning::Feature branch does not contain any changes to the website."
test_go:
name: Run Go Tests
runs-on: ${{ matrix.os }}
# Run when:
# - the event is a pull_request (opened/synchronize/reopened) OR
# - the event is a pull_request_review AND the review state is 'approved'
# plus other existing filters (not the update-sponsors branch, repo and base_ref)
if: >
github.repository == 'wailsapp/wails' &&
github.base_ref == 'master' &&
github.event.pull_request.head.ref != 'update-sponsors' &&
(
github.event_name == 'pull_request' ||
(github.event_name == 'pull_request_review' && github.event.review.state == 'approved')
)
strategy:
matrix:
os: [ubuntu-22.04, windows-latest, macos-latest, ubuntu-24.04]
go-version: ['1.23']
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install linux dependencies (22.04)
if: matrix.os == 'ubuntu-22.04'
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
- name: Install linux dependencies (24.04)
if: matrix.os == 'ubuntu-24.04'
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev build-essential pkg-config
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Run tests (mac)
if: matrix.os == 'macos-latest'
env:
CGO_LDFLAGS: -framework UniformTypeIdentifiers -mmacosx-version-min=10.13
working-directory: ./v2
run: go test -v ./...
- name: Run tests (!mac)
if: matrix.os != 'macos-latest' && matrix.os != 'ubuntu-24.04'
working-directory: ./v2
run: go test -v ./...
- name: Run tests (Ubuntu 24.04)
if: matrix.os == 'ubuntu-24.04'
working-directory: ./v2
run: go test -v -tags webkit2_41 ./...
# This job will run instead of test_go for the update-sponsors branch
skip_tests:
name: Skip Tests (Sponsor Update)
if: github.event.pull_request.head.ref == 'update-sponsors'
runs-on: ubuntu-latest
steps:
- name: Skip tests for sponsor updates
run: |
echo "Skipping tests for sponsor update branch"
echo "This is an automated update of the sponsors image."
continue-on-error: true

61
.github/workflows/pr.yml vendored Normal file
View file

@ -0,0 +1,61 @@
name: PR Checks
on:
pull_request:
pull_request_review:
types: [submitted]
jobs:
check_docs:
name: Check Docs
if: ${{github.repository == 'wailsapp/wails' && contains(github.head_ref,'feature/')}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v11.1
id: verify-changed-files
with:
files: |
website/**/*.mdx
website/**/*.md
- name: Run step only when files change.
if: steps.verify-changed-files.outputs.files_changed != 'true'
run: |
echo "::warning::Feature branch does not contain any changes to the website."
test_go:
name: Run Go Tests
runs-on: ${{ matrix.os }}
if: github.event.review.state == 'approved'
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
go-version: [1.18, 1.19]
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Install linux dependencies
if: matrix.os == 'ubuntu-latest'
run: sudo apt-get update -y && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev build-essential pkg-config
- name: Setup Go
uses: actions/setup-go@v3
with:
go-version: ${{ matrix.go-version }}
- name: Run tests (mac)
if: matrix.os == 'macos-latest'
env:
CGO_LDFLAGS: -framework UniformTypeIdentifiers -mmacosx-version-min=10.13
working-directory: ./v2
run: go test -v ./...
- name: Run tests (!mac)
if: matrix.os != 'macos-latest'
working-directory: ./v2
run: go test -v ./...

37
.github/workflows/push.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: Push Checks
on:
push:
branches: [master]
jobs:
push_files_to_crowdin:
name: Push files to Crowdin
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Verify Changed files
uses: tj-actions/verify-changed-files@v11.1
id: verify-changed-files
with:
files: |
website/**/*.mdx
website/**/*.md
website/**/*.json
- name: Set node
if: steps.verify-changed-files.outputs.files_changed != 'true'
uses: actions/setup-node@v2
with:
node-version: 16.x
- name: Push files
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
run: |
cd website
corepack enable
pnpm install
pnpm run crowdin push -b master

View file

@ -1,25 +0,0 @@
on:
workflow_dispatch: {}
pull_request: {}
push:
branches:
- main
- master
- v3-alpha
paths:
- .github/workflows/semgrep.yml
schedule:
# random HH:MM to avoid a load spike on GitHub Actions at 00:00
- cron: 14 16 * * *
name: Semgrep
jobs:
semgrep:
name: semgrep/ci
runs-on: ubuntu-24.04
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
container:
image: returntocorp/semgrep
steps:
- uses: actions/checkout@v3
- run: semgrep ci

View file

@ -1,57 +0,0 @@
name: Mark and Close Stale Issues
on:
schedule:
- cron: '0 1 * * *' # Run at 1 AM UTC every day
workflow_dispatch: # Allow manual triggering
jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
with:
# General settings
repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 45
days-before-close: 10
stale-issue-label: 'stale'
operations-per-run: 250 # Increased from 50 to 250
# Issue specific settings
stale-issue-message: |
This issue has been automatically marked as stale because it has not had recent activity.
It will be closed if no further activity occurs within the next 10 days.
If this issue is still relevant, please add a comment to keep it open.
Thank you for your contributions.
close-issue-message: |
This issue has been automatically closed due to lack of activity.
Please feel free to reopen it if it's still relevant.
# PR specific settings - We will not mark PRs as stale
days-before-pr-stale: -1 # Disable PR staling
days-before-pr-close: -1 # Disable PR closing
# Exemptions
exempt-issue-labels: 'pinned,security,onhold,inprogress,Selected For Development,bug,enhancement,v3-alpha,high-priority'
exempt-all-issue-milestones: true
exempt-all-issue-assignees: true
# Protection for existing issues
exempt-issue-created-before: '2024-01-01T00:00:00Z'
start-date: '2025-06-01T00:00:00Z' # Don't start checking until June 1, 2025
# Only process issues, not PRs
only-labels: ''
any-of-labels: ''
remove-stale-when-updated: true
# Debug options
debug-only: false # Set to true to test without actually marking issues
ascending: true # Process older issues first

View file

@ -1,9 +1,7 @@
name: Sync Translations
name: Sync Translated Documents
on:
workflow_dispatch:
schedule:
- cron: "0 0 * * *"
jobs:
sync-translated-documents:
@ -12,30 +10,22 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Setup Nodejs
- name: Set node
uses: actions/setup-node@v2
with:
node-version: 20.x
- name: Install Task
uses: arduino/setup-task@v1
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
node-version: 16.x
- name: Sync Translated Documents
run: cd scripts && chmod 755 ./sync-translated-documents.sh && ./sync-translated-documents.sh
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
working-directory: ./website
run: task crowdin:pull
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
commit-message: "docs: sync translations"
title: "docs: sync translations"
commit-message: "docs: sync documents"
title: "docs: sync documents"
body: "- [x] Sync translated documents"
branch: chore/sync-translations
labels: translation
branch: feature/documents
delete-branch: true
draft: true

View file

@ -1,216 +0,0 @@
name: Test Nightly Releases (Dry Run)
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode (no actual releases)'
required: false
default: true
type: boolean
test_branch:
description: 'Branch to test against'
required: false
default: 'master'
type: string
env:
GO_VERSION: '1.24'
jobs:
test-permissions:
name: Test Release Permissions
runs-on: ubuntu-latest
outputs:
authorized: ${{ steps.check.outputs.authorized }}
steps:
- name: Check if user is authorized
id: check
run: |
# Test authorization logic
AUTHORIZED_USERS="leaanthony"
if [[ "$AUTHORIZED_USERS" == *"${{ github.actor }}"* ]]; then
echo "✅ User ${{ github.actor }} is authorized"
echo "authorized=true" >> $GITHUB_OUTPUT
else
echo "❌ User ${{ github.actor }} is not authorized"
echo "authorized=false" >> $GITHUB_OUTPUT
fi
test-changelog-extraction:
name: Test Changelog Extraction
runs-on: ubuntu-latest
needs: test-permissions
if: needs.test-permissions.outputs.authorized == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.test_branch }}
fetch-depth: 0
- name: Test v2 changelog extraction
run: |
echo "🧪 Testing v2 changelog extraction..."
CHANGELOG_FILE="website/src/pages/changelog.mdx"
if [ ! -f "$CHANGELOG_FILE" ]; then
echo "❌ v2 changelog file not found"
exit 1
fi
# Extract unreleased section
awk '
/^## \[Unreleased\]/ { found=1; next }
found && /^## / { exit }
found && !/^$/ { print }
' $CHANGELOG_FILE > v2_release_notes.md
echo "📝 v2 changelog content (first 10 lines):"
head -10 v2_release_notes.md || echo "No content found"
echo "Total lines: $(wc -l < v2_release_notes.md)"
- name: Test v3 changelog extraction (if accessible)
run: |
echo "🧪 Testing v3 changelog extraction..."
if git show v3-alpha:docs/src/content/docs/changelog.mdx > /dev/null 2>&1; then
echo "✅ v3 changelog accessible"
git show v3-alpha:docs/src/content/docs/changelog.mdx | awk '
/^## \[Unreleased\]/ { found=1; next }
found && /^## / { exit }
found && !/^$/ { print }
' > v3_release_notes.md
echo "📝 v3 changelog content (first 10 lines):"
head -10 v3_release_notes.md || echo "No content found"
echo "Total lines: $(wc -l < v3_release_notes.md)"
else
echo "⚠️ v3 changelog not accessible from current context"
fi
test-version-detection:
name: Test Version Detection
runs-on: ubuntu-latest
needs: test-permissions
if: needs.test-permissions.outputs.authorized == 'true'
outputs:
v2_current_version: ${{ steps.versions.outputs.v2_current }}
v2_next_patch: ${{ steps.versions.outputs.v2_next_patch }}
v2_next_minor: ${{ steps.versions.outputs.v2_next_minor }}
v2_next_major: ${{ steps.versions.outputs.v2_next_major }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test version detection logic
id: versions
run: |
echo "🧪 Testing version detection..."
# Test v2 version parsing
if [ -f "v2/cmd/wails/internal/version.txt" ]; then
CURRENT_V2=$(cat v2/cmd/wails/internal/version.txt | sed 's/^v//')
echo "Current v2 version: v$CURRENT_V2"
echo "v2_current=v$CURRENT_V2" >> $GITHUB_OUTPUT
# Parse and increment
IFS='.' read -ra VERSION_PARTS <<< "$CURRENT_V2"
MAJOR=${VERSION_PARTS[0]}
MINOR=${VERSION_PARTS[1]}
PATCH=${VERSION_PARTS[2]}
PATCH_VERSION="v$MAJOR.$MINOR.$((PATCH + 1))"
MINOR_VERSION="v$MAJOR.$((MINOR + 1)).0"
MAJOR_VERSION="v$((MAJOR + 1)).0.0"
echo "v2_next_patch=$PATCH_VERSION" >> $GITHUB_OUTPUT
echo "v2_next_minor=$MINOR_VERSION" >> $GITHUB_OUTPUT
echo "v2_next_major=$MAJOR_VERSION" >> $GITHUB_OUTPUT
echo "✅ Patch: v$CURRENT_V2 → $PATCH_VERSION"
echo "✅ Minor: v$CURRENT_V2 → $MINOR_VERSION"
echo "✅ Major: v$CURRENT_V2 → $MAJOR_VERSION"
else
echo "❌ v2 version file not found"
fi
test-commit-analysis:
name: Test Commit Analysis
runs-on: ubuntu-latest
needs: test-permissions
if: needs.test-permissions.outputs.authorized == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Test commit analysis
run: |
echo "🧪 Testing commit analysis..."
# Get recent commits for testing
echo "Recent commits:"
git log --oneline -10
# Test conventional commit detection
RECENT_COMMITS=$(git log --oneline --since="7 days ago")
echo "Commits from last 7 days:"
echo "$RECENT_COMMITS"
# Analyze for release type
RELEASE_TYPE="patch"
if echo "$RECENT_COMMITS" | grep -q "feat!\|fix!\|BREAKING CHANGE:"; then
RELEASE_TYPE="major"
elif echo "$RECENT_COMMITS" | grep -q "feat\|BREAKING CHANGE"; then
RELEASE_TYPE="minor"
fi
echo "✅ Detected release type: $RELEASE_TYPE"
test-summary:
name: Test Summary
runs-on: ubuntu-latest
needs: [test-permissions, test-changelog-extraction, test-version-detection, test-commit-analysis]
if: always()
steps:
- name: Print test results
run: |
echo "# 🧪 Nightly Release Workflow Test Results" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${{ needs.test-permissions.result }}" == "success" ]; then
echo "✅ **Permissions Test**: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Permissions Test**: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.test-changelog-extraction.result }}" == "success" ]; then
echo "✅ **Changelog Extraction**: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Changelog Extraction**: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.test-version-detection.result }}" == "success" ]; then
echo "✅ **Version Detection**: Passed" >> $GITHUB_STEP_SUMMARY
echo " - Current v2: ${{ needs.test-version-detection.outputs.v2_current_version }}" >> $GITHUB_STEP_SUMMARY
echo " - Next patch: ${{ needs.test-version-detection.outputs.v2_next_patch }}" >> $GITHUB_STEP_SUMMARY
echo " - Next minor: ${{ needs.test-version-detection.outputs.v2_next_minor }}" >> $GITHUB_STEP_SUMMARY
echo " - Next major: ${{ needs.test-version-detection.outputs.v2_next_major }}" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Version Detection**: Failed" >> $GITHUB_STEP_SUMMARY
fi
if [ "${{ needs.test-commit-analysis.result }}" == "success" ]; then
echo "✅ **Commit Analysis**: Passed" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **Commit Analysis**: Failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "**Note**: This was a dry-run test. No actual releases were created." >> $GITHUB_STEP_SUMMARY

View file

@ -1,129 +0,0 @@
name: Auto Release on Changelog Update
on:
push:
branches:
- v3-alpha
paths:
- 'v3/UNRELEASED_CHANGELOG.md'
workflow_dispatch:
inputs:
dry_run:
description: 'Run in dry-run mode (no actual release)'
required: false
default: false
type: boolean
jobs:
check-permissions:
name: Check Release Permissions
runs-on: ubuntu-latest
outputs:
authorized: ${{ steps.check.outputs.authorized }}
steps:
- name: Check if user is authorized for releases
id: check
run: |
# Only allow specific users to trigger releases
AUTHORIZED_USERS="leaanthony"
if [[ "$AUTHORIZED_USERS" == *"${{ github.actor }}"* ]]; then
echo "✅ User ${{ github.actor }} is authorized for releases"
echo "authorized=true" >> $GITHUB_OUTPUT
else
echo "❌ User ${{ github.actor }} is not authorized for releases"
echo "authorized=false" >> $GITHUB_OUTPUT
fi
trigger-release:
name: Trigger v3-alpha Release
permissions:
contents: read
actions: write
runs-on: ubuntu-latest
needs: check-permissions
if: needs.check-permissions.outputs.authorized == 'true'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
ref: v3-alpha
fetch-depth: 0
token: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
- name: Check for unreleased changelog content
id: changelog_check
run: |
echo "🔍 Checking UNRELEASED_CHANGELOG.md for content..."
cd v3
# Check if UNRELEASED_CHANGELOG.md has actual content beyond the template
if [ -f "UNRELEASED_CHANGELOG.md" ]; then
# Use a simple check for actual content (bullet points starting with -)
CONTENT_LINES=$(grep -E "^\s*-\s+[^[:space:]]" UNRELEASED_CHANGELOG.md | wc -l)
if [ "$CONTENT_LINES" -gt 0 ]; then
echo "✅ Found $CONTENT_LINES content lines in UNRELEASED_CHANGELOG.md"
echo "has_content=true" >> $GITHUB_OUTPUT
else
echo " No actual content found in UNRELEASED_CHANGELOG.md"
echo "has_content=false" >> $GITHUB_OUTPUT
fi
else
echo "❌ UNRELEASED_CHANGELOG.md not found"
echo "has_content=false" >> $GITHUB_OUTPUT
fi
- name: Trigger nightly release workflow
if: steps.changelog_check.outputs.has_content == 'true'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.WAILS_REPO_TOKEN || github.token }}
script: |
const response = await github.rest.actions.createWorkflowDispatch({
owner: context.repo.owner,
repo: context.repo.repo,
workflow_id: 'nightly-release-v3.yml',
ref: 'v3-alpha',
inputs: {
force_release: 'true',
dry_run: '${{ github.event.inputs.dry_run || "false" }}'
}
});
console.log('🚀 Successfully triggered nightly release workflow');
console.log(`Workflow dispatch response status: ${response.status}`);
// Create a summary
core.summary
.addHeading('🚀 Auto Release Triggered')
.addRaw('The v3-alpha release workflow has been automatically triggered due to changes in UNRELEASED_CHANGELOG.md')
.addTable([
[{data: 'Trigger', header: true}, {data: 'Value', header: true}],
['Repository', context.repo.repo],
['Branch', 'v3-alpha'],
['Actor', context.actor],
['Dry Run', '${{ github.event.inputs.dry_run || "false" }}'],
['Force Release', 'true']
])
.addRaw('\n---\n*This release was automatically triggered by the unreleased-changelog-trigger workflow*')
.write();
- name: No content found
if: steps.changelog_check.outputs.has_content == 'false'
run: |
echo " No content found in UNRELEASED_CHANGELOG.md, skipping release trigger"
echo "## No Release Triggered" >> $GITHUB_STEP_SUMMARY
echo "**Reason:** UNRELEASED_CHANGELOG.md does not contain actual changelog content" >> $GITHUB_STEP_SUMMARY
echo "**Action:** No release workflow was triggered" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "To trigger a release, add actual changelog entries to the UNRELEASED_CHANGELOG.md file." >> $GITHUB_STEP_SUMMARY
- name: Unauthorized user
if: needs.check-permissions.outputs.authorized == 'false'
run: |
echo "❌ User ${{ github.actor }} is not authorized to trigger releases"
echo "## ❌ Unauthorized Release Attempt" >> $GITHUB_STEP_SUMMARY
echo "**User:** ${{ github.actor }}" >> $GITHUB_STEP_SUMMARY
echo "**Action:** Release trigger was blocked due to insufficient permissions" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Only authorized users can trigger automatic releases via changelog updates." >> $GITHUB_STEP_SUMMARY

View file

@ -1,41 +0,0 @@
name: Upload Source Documents
on:
push:
branches: [master]
workflow_dispatch:
jobs:
push_files_to_crowdin:
name: Push files to Crowdin
if: github.repository == 'wailsapp/wails'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Verify Changed files
id: changed-files
uses: step-security/changed-files@3dbe17c78367e7d60f00d78ae6781a35be47b4a1 # v45.0.1
with:
files: |
website/**/*.mdx
website/**/*.md
website/**/*.json
- name: Setup Nodejs
uses: actions/setup-node@v2
with:
node-version: 20.x
- name: Setup Task
uses: arduino/setup-task@v1
with:
version: 3.x
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Upload source documents
if: steps.changed-files.outputs.any_changed == 'true'
env:
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
working-directory: ./website
run: task crowdin:push

5
.gitignore vendored
View file

@ -34,8 +34,3 @@ v2/cmd/wails/internal/commands/initialise/templates/testtemplates/
/v3/.task
/v3/examples/build/bin/testapp
/websitev3/site/
/v3/examples/plugins/bin/testapp
# Temporary called mkdocs, should be renamed to more standard -website or similar
/mkdocs-website/site

34
.goreleaser.yml Normal file
View file

@ -0,0 +1,34 @@
# This is an example goreleaser.yaml file with some sane defaults.
# Make sure to check the documentation at http://goreleaser.com
builds:
- env:
- CGO_ENABLED=0
goos:
- windows
- linux
- darwin
goarch:
- 386
- amd64
ignore:
- goos: darwin
goarch: 386
main: ./cmd/wails/main.go
archive:
replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "{{ .Tag }}-next"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

6
.hound.yml Normal file
View file

@ -0,0 +1,6 @@
jshint:
config_file: .jshintrc
eslint:
enabled: true
config_file: .eslintrc
ignore_file: .eslintignore

3
.jshintrc Normal file
View file

@ -0,0 +1,3 @@
{
"esversion": 6
}

View file

@ -1,3 +0,0 @@
website
v2
v3

View file

@ -1,6 +0,0 @@
overrides:
- files:
- "**/*.md"
options:
printWidth: 80
proseWrap: always

View file

@ -1,8 +0,0 @@
modules = ["go-1.21", "web", "nodejs-20"]
run = "go run v2/cmd/wails/main.go"
[nix]
channel = "stable-24_05"
[deployment]
run = ["sh", "-c", "go run v2/cmd/wails/main.go"]

View file

@ -1,160 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Erschaffe Desktop Anwendungen mit Go & Web Technologien.
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md)
</samp>
</strong>
</div>
## Inhaltsverzeichnis
- [Inhaltsverzeichnis](#inhaltsverzeichnis)
- [Einführung](#einführung)
- [Funktionen](#funktionen)
- [Roadmap](#roadmap)
- [Loslegen](#loslegen)
- [Sponsoren](#sponsoren)
- [FAQ](#faq)
- [Sterne Überblick](#sterne-überblick)
- [Mitwirkende](#mitwirkende)
- [Lizenz](#lizenz)
- [Inspiration](#inspiration)
## Einführung
Die herkömmliche Methode zur Bereitstellung von Web-Interfaces für Go ist über einen eingebauten Webserver.
Wails nutzt einen anderen Weg. Es kann sowohl Go-Code als auch ein Web-Frontend in eine einzige Datei bauen.
Beigelieferte Werkzeuge übernehmen die Projekterstellung, den Kompilierungsprozess und das bauen.
Du musst nur kreativ werden.
## Funktionen
- Nutze Standard Go für das Backend
- Nutze eine Frontend Technologie mit der du dich bereits auskennst um dein UI zu bauen.
- Erschaffe schnell und einfach Frontends mit vorgefertigten Vorlagen für deine Go-Programme
- Nutze Javascript um Go Methoden aufzurufen
- Automatisch generierte Typescript Definitionen für deine Go Strukturen und Methoden
- Native Dialoge und Menüs
- Native Dark-/Lightmode Unterstützung
- Unterstützt moderne Transluzenz- und Milchglaseffekte
- Vereinheitlichtes Eventsystem zwischen Go und Javascript
- Leistungsstarkes CLI-Tool zum einfachen erstellen und bauen von Projekten
- Multiplattformen
- Nutze native Render-Engines - _keine eingebetteten Browser_!
### Roadmap
Die Projekt Roadmap kann [hier](https://github.com/wailsapp/wails/discussions/1484) gefunden werden. Bitte lies diese
durch bevor du eine Idee vorschlägst
## Loslegen
Die Installationsinstruktionen sind auf der [offiziellen Website](https://wails.io/docs/gettingstarted/installation).
## Sponsoren
Dieses Projekt wird von diesen freundlichen Leuten und Firmen unterstützt:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## FAQ
- Ist das eine Alternative zu Electron?
Hängt von deinen Anforderungen ab. Wails wurde entwickelt um das Go-Programmieren leicht zu machen und effiziente
Desktop-Anwendungen zu erstellen oder ein Frontend zu einer bestehenden Anwendung hinzuzufügen.
Wails bietet native Elemente wie Dialoge und Menüs und könnte somit als eine leichte effiziente Electron-Alternative
betrachtet werden.
- Für wen ist dieses projekt geeignet?
Go Entwickler, die ein HTML/CSS/JS-Frontend in ihre Anwendung integrieren möchten, ohne einen Webserver zu erstellen und
einen Browser öffnen zu müssen, um dieses zu sehen
- Wie kam es zu diesem Namen?
Als ich WebView sah dachte ich "Was ich wirklich will, ist ein Werkzeug für die Erstellung von WebView Anwendungen so wie Rails für Ruby".
Also war es zunächst ein Wortspiel (Webview on Rails). Zufälligerweise ist es auch ein Homophon des englischen Namens des [Landes](https://en.wikipedia.org/wiki/Wales), aus dem ich komme.
Also ist es dabei geblieben.
## Sterne Überblick
<a href="https://star-history.com/#wailsapp/wails&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
</picture>
</a>
## Mitwirkende
Die Liste der Mitwirkenden wird zu groß für diese Readme. All die fantastischen Menschen, die zu diesem
Projekt beigetragen haben, haben [hier](https://wails.io/credits#contributors) ihre eigene Seite.
## Lizenz
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Inspiration
Dieses Projekt wurde hauptsächlich zu den folgenden Alben entwickelt
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -1,169 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Construye aplicaciones de escritorio usando Go y tecnologías web.
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## Tabla de Contenidos
- [Tabla de Contenidos](#tabla-de-contenidos)
- [Introducción](#introducción)
- [Funcionalidades](#funcionalidades)
- [Plan de Trabajo](#plan-de-trabajo)
- [Empezando](#empezando)
- [Patrocinadores](#patrocinadores)
- [Preguntas Frecuentes](#preguntas-frecuentes)
- [Estrellas a lo Largo del Tiempo](#estrellas-a-lo-largo-del-tiempo)
- [Colaboradores](#colaboradores)
- [Licencia](#licencia)
- [Inspiración](#inspiración)
## Introducción
El método tradicional para proveer una interfaz web en programas hechos con Go
es a través del servidor web incorporado. Wails ofrece un enfoque diferente al
permitir combinar el código hecho en Go con un frontend web en un solo archivo
binario. Las herramientas que proporcionamos facilitan este trabajo para ti, al
crear, compilar y empaquetar tu proyecto. ¡Lo único que debes hacer es ponerte
creativo!
## Funcionalidades
- Utiliza Go estándar para el backend
- Utiliza cualquier tecnología frontend con la que ya estés familiarizado para
construir tu interfaz de usuario
- Crea rápidamente interfaces de usuario enriquecidas para tus programas en Go
utilizando plantillas predefinidas
- Invoca fácilmente métodos de Go desde Javascript
- Definiciones de Typescript generadas automáticamente para tus structs y
métodos de Go
- Diálogos y menús nativos
- Soporte nativo de modo oscuro / claro
- Soporte de translucidez y efectos de ventana esmerilada
- Sistema de eventos unificado entre Go y Javascript
- Herramienta CLI potente para generar y construir tus proyectos rápidamente
- Multiplataforma
- Usa motores de renderizado nativos - ¡_sin navegador integrado_!
### Plan de Trabajo
El plan de trabajo se puede encontrar
[aqui](https://github.com/wailsapp/wails/discussions/1484). Por favor,
consúltalo antes de abrir una solicitud de mejora.
## Empezando
Las instrucciones de instalacion se encuentran en nuestra
[pagina web oficial](https://wails.io/docs/gettingstarted/installation).
## Patrocinadores
Este Proyecto cuenta con el apoyo de estas amables personas/ compañías:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## Preguntas Frecuentes
- ¿Es esta una alternativa a Electron?
Depende de tus requisitos. Está diseñado para facilitar a los programadores de
Go la creación de aplicaciones de escritorio livianas o agregar una interfaz
gráfica a sus aplicaciones existentes. Wails ofrece elementos nativos como
menús y diálogos, por lo que podría considerarse una alternativa liviana a
Electron.
- ¿A quien esta dirigido este proyecto?
El proyecto esta dirigido a programadores de Go que desean integrar una
interfaz HMTL/JS/CSS en sus aplicaciones, sin tener que recurrir a la creación
de un servidor y abrir el navegador para visualizarla.
- ¿Cual es el significado del nombre?
Cuando vi WebView, pensé: "Lo que realmente quiero es una herramienta para
construir una aplicación WebView, algo similar a lo que Rails es para Ruby".
Así que inicialmente fue un juego de palabras (WebView en Rails). Además, por
casualidad, también es homófono del nombre en inglés del
[país](https://en.wikipedia.org/wiki/Wales) del que provengo. Así que se quedó
con ese nombre.
## Estrellas a lo Largo del Tiempo
[![Star History Chart](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
## Colaboradores
¡La lista de colaboradores se está volviendo demasiado grande para el archivo
readme! Todas las personas increíbles que han contribuido a este proyecto tienen
su propia página [aqui](https://wails.io/credits#contributors).
## Licencia
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Inspiración
Este proyecto fue construido mientras se escuchaban estos álbumes:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
[Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -1,144 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Créer des applications de bureau avec Go et les technologies Web.
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## Sommaire
- [Sommaire](#sommaire)
- [Introduction](#introduction)
- [Fonctionnalités](#fonctionnalités)
- [Feuille de route](#feuille-de-route)
- [Démarrage](#démarrage)
- [Les sponsors](#les-sponsors)
- [Foire aux questions](#foire-aux-questions)
- [Les étoiles au fil du temps](#les-étoiles-au-fil-du-temps)
- [Les contributeurs](#les-contributeurs)
- [License](#license)
- [Inspiration](#inspiration)
## Introduction
La méthode traditionnelle pour fournir des interfaces web aux programmes Go consiste à utiliser un serveur web intégré. Wails propose une approche différente : il offre la possibilité d'intégrer à la fois le code Go et une interface web dans un seul binaire. Des outils sont fournis pour vous faciliter la tâche en gérant la création, la compilation et le regroupement des projets. Il ne vous reste plus qu'à faire preuve de créativité!
## Fonctionnalités
- Utiliser Go pour le backend
- Utilisez n'importe quelle technologie frontend avec laquelle vous êtes déjà familier pour construire votre interface utilisateur.
- Créez rapidement des interfaces riches pour vos programmes Go à l'aide de modèles prédéfinis.
- Appeler facilement des méthodes Go à partir de Javascript
- Définitions Typescript auto-générées pour vos structures et méthodes Go
- Dialogues et menus natifs
- Prise en charge native des modes sombre et clair
- Prise en charge des effets modernes de translucidité et de "frosted window".
- Système d'événements unifié entre Go et Javascript
- Outil puissant pour générer et construire rapidement vos projets
- Multiplateforme
- Utilise des moteurs de rendu natifs - _pas de navigateur intégré_ !
### Feuille de route
La feuille de route du projet peut être consultée [ici](https://github.com/wailsapp/wails/discussions/1484). Veuillez consulter avant d'ouvrir une demande d'amélioration.
## Démarrage
Les instructions d'installation se trouvent sur le site [site officiel](https://wails.io/docs/gettingstarted/installation).
## Les sponsors
Ce projet est soutenu par ces personnes aimables et entreprises:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## Foire aux questions
- S'agit-il d'une alternative à Electron ?
Cela dépend de vos besoins. Il est conçu pour permettre aux programmeurs Go de créer facilement des applications de bureau légères ou d'ajouter une interface à leurs applications existantes. Wails offre des éléments natifs tels que des menus et des boîtes de dialogue, il peut donc être considéré comme une alternative légère à electron.
- À qui s'adresse ce projet ?
Les programmeurs Go qui souhaitent intégrer une interface HTML/JS/CSS à leurs applications, sans avoir à créer un serveur et à ouvrir un navigateur pour l'afficher.
- Pourquoi ce nom ??
Lorsque j'ai vu WebView, je me suis dit : "Ce que je veux vraiment, c'est un outil pour construire une application WebView, un peu comme Rails l'est pour Ruby". Au départ, il s'agissait donc d'un jeu de mots (Webview on Rails). Il se trouve que c'est aussi un homophone du nom anglais du [Pays](https://en.wikipedia.org/wiki/Wales) d'où je viens. Il s'est donc imposé.
## Les étoiles au fil du temps
[![Graphique de l'histoire des étoiles](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
## Les contributeurs
La liste des contributeurs devient trop importante pour le readme ! Toutes les personnes extraordinaires qui ont contribué à ce projet ont leur propre page [ici](https://wails.io/credits#contributors).
## License
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Inspiration
Ce projet a été principalement codé sur les albums suivants :
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -26,8 +26,8 @@
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
<a href="https://app.slack.com/client/T029RQSE6/CJ4P9F7MZ">
<img alt="Slack" src="https://img.shields.io/badge/slack-gophers%2Fwails%20-blue?logo=slack"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
@ -42,10 +42,7 @@
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) · [한국어](README.ko.md)
</samp>
</strong>
@ -55,16 +52,16 @@
- [目次](#目次)
- [はじめに](#はじめに)
- [特徴](#特徴)
- [公式サイト](#公式サイト)
- [ロードマップ](#ロードマップ)
- [始め方](#始め方)
- [特徴](#特徴)
- [スポンサー](#スポンサー)
- [始め方](#始め方)
- [FAQ](#faq)
- [スター数の推移](#スター数の推移)
- [コントリビューター](#コントリビューター)
- [特記事項](#特記事項)
- [スペシャルサンクス](#スペシャルサンクス)
- [ライセンス](#ライセンス)
- [インスピレーション](#インスピレーション)
## はじめに
@ -72,35 +69,44 @@ Go プログラムにウェブインタフェースを提供する従来の方
Wails では Go のコードとウェブフロントエンドを単一のバイナリにまとめる機能を提供します。
また、プロジェクトの作成、コンパイル、ビルドを行うためのツールが提供されています。あなたがすべきことは創造性を発揮することです!
## 特徴
### 公式サイト
- バックエンドには Go を利用しています
- 使い慣れたフロントエンド技術を利用して UI を構築できます
- あらかじめ用意されたテンプレートを利用することで、リッチなフロントエンドを備えた Go プログラムを素早く作成できます
- JavaScript から Go のメソッドを簡単に呼び出すことができます
- あなたの書いた Go の構造体やメソットに応じた TypeScript の定義が自動生成されます
- ネイティブのダイアログとメニューが利用できます
- ネイティブなダーク/ライトモードをサポートします
- モダンな半透明や「frosted window」エフェクトをサポートしています
- Go と JavaScript 間で統一されたイベント・システムを備えています
- プロジェクトを素早く生成して構築する強力な cli ツールを用意しています
- マルチプラットフォームに対応しています
- ネイティブなレンダリングエンジンを使用しています - _つまりブラウザを埋め込んでいるわけではありません_
Version 2:
Wails v2 が 3 つのプラットフォームでベータ版としてリリースされました。興味のある方は[新しいウェブサイト](https://wails.io)をご覧ください。
レガシー版 v1:
レガシー版 v1 のドキュメントは[https://wails.app](https://wails.app)で見ることができます。
### ロードマップ
プロジェクトのロードマップは[こちら](https://github.com/wailsapp/wails/discussions/1484)になります。
機能拡張のリクエストを出す前にご覧ください。
## 始め方
## 特徴
インストール方法は[公式サイト](https://wails.io/docs/gettingstarted/installation)に掲載されています。
- バックエンドには Go を利用しています
- 使い慣れたフロントエンド技術を利用して UI を構築できます
- あらかじめ用意されたテンプレートを利用することで、リッチなフロントエンドを備えた Go プログラムを作成できます
- JavaScript から Go のメソッドを簡単に呼び出すことができます
- あなたの書いた Go の構造体やメソットに応じた TypeScript の定義が自動生成されます
- ネイティブのダイアログとメニューが利用できます
- モダンな半透明や「frosted window」エフェクトをサポートしています
- Go と JavaScript 間で統一されたイベント・システムを備えています
- プロジェクトを素早く生成して構築する強力な cli ツールを用意しています
- マルチプラットフォームに対応しています
- ネイティブなレンダリングエンジンを使用しています - _つまりブラウザを埋め込んでいるわけではありません_
## スポンサー
このプロジェクトは、以下の方々・企業によって支えられています。
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
## 始め方
インストール方法は[公式サイト](https://wails.io/docs/gettingstarted/installation)に掲載されています。
## FAQ
- Electron の代替品になりますか?
@ -121,18 +127,20 @@ Wails では Go のコードとウェブフロントエンドを単一のバイ
## スター数の推移
[![Star History Chart](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
[![スター数の推移](https://starchart.cc/wailsapp/wails.svg)](https://starchart.cc/wailsapp/wails)
## コントリビューター
貢献してくれた方のリストが大きくなりすぎて、readme に入りきらなくなりました!
このプロジェクトに貢献してくれた素晴らしい方々のページは[こちら](https://wails.io/credits#contributors)です。
## ライセンス
## 特記事項
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
このプロジェクトは以下の方々の協力がなければ、実現しなかったと思います。
## インスピレーション
- [Dustin Krysak](https://wiki.ubuntu.com/bashfulrobot) - 彼のサポートとフィードバックはとても大きいものでした。
- [Serge Zaitsev](https://github.com/zserge) - Wails のウィンドウで使用している[Webview](https://github.com/zserge/webview)の作者です。
- [Byron](https://github.com/bh90210) - 時には Byron が一人でこのプロジェクトを存続させてくれたこともありました。彼の素晴らしいインプットがなければ v1 に到達することはなかったでしょう。
プロジェクトを進める際に、以下のアルバムたちも支えてくれています。
@ -150,3 +158,20 @@ Wails では Go のコードとウェブフロントエンドを単一のバイ
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)
## スペシャルサンクス
<p align="center" style="text-align: center">
<a href="https://pace.dev"><img src="/assets/images/pace.jpeg"/></a><br/>
このプロジェクトを後援し、WailsをApple Siliconに移植する取り組みを支援してくれた <a href="https://pace.dev">Pace</a> に <i>とても</i>感謝しています!<br/><br/>
パワフルで素早く簡単に使えるプロジェクト管理ツールをお探しなら、ぜひチェックしてみてください!<br/><br/>
</p>
<p align="center" style="text-align: center">
ライセンスを提供していただいたJetBrains社に感謝します<br/><br/>
ロゴをクリックして、感謝の気持ちを伝えてください!<br/><br/>
<a href="https://www.jetbrains.com?from=Wails"><img src="/assets/images/jetbrains-grayscale.png" width="30%"></a>
</p>
## ライセンス
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)

View file

@ -26,8 +26,8 @@
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
<a href="https://app.slack.com/client/T029RQSE6/CJ4P9F7MZ">
<img alt="Slack" src="https://img.shields.io/badge/slack-gophers%2Fwails%20-blue?logo=slack"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
@ -42,10 +42,7 @@
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) · [한국어](README.ko.md)
</samp>
</strong>
@ -60,7 +57,6 @@
- [시작하기](#시작하기)
- [스폰서](#스폰서)
- [FAQ](#faq)
- [Stargazers 성장 추세](#stargazers-성장-추세)
- [기여자](#기여자)
- [라이센스](#라이센스)
- [영감](#영감)

View file

@ -24,8 +24,8 @@
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
<a href="https://app.slack.com/client/T029RQSE6/CJ4P9F7MZ">
<img alt="Slack" src="https://img.shields.io/badge/slack-gophers%2Fwails%20-blue?logo=slack"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
@ -40,10 +40,7 @@
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) · [한국어](README.ko.md)
</samp>
</strong>
@ -58,7 +55,6 @@
- [Getting Started](#getting-started)
- [Sponsors](#sponsors)
- [FAQ](#faq)
- [Stargazers over time](#stargazers-over-time)
- [Contributors](#contributors)
- [License](#license)
- [Inspiration](#inspiration)
@ -87,7 +83,7 @@ make this easy for you by handling project creation, compilation and bundling. A
### Roadmap
The project roadmap may be found [here](https://github.com/wailsapp/wails/discussions/1484). Please consult
it before creating an enhancement request.
this before open up an enhancement request.
## Getting Started
@ -98,10 +94,6 @@ The installation instructions are on the [official website](https://wails.io/doc
This project is supported by these kind people / companies:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
## Powered By
[![JetBrains logo.](https://resources.jetbrains.com/storage/products/company/brand/logos/jetbrains.svg)](https://jb.gg/OpenSource)
## FAQ
- Is this an alternative to Electron?
@ -123,13 +115,7 @@ This project is supported by these kind people / companies:
## Stargazers over time
<a href="https://star-history.com/#wailsapp/wails&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
</picture>
</a>
[![Star History Chart](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
## Contributors

View file

@ -1,151 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Crie aplicativos de desktop usando Go e tecnologias Web.
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## Índice
- [Índice](#índice)
- [Introdução](#introdução)
- [Recursos e funcionalidades](#recursos-e-funcionalidades)
- [Plano de trabalho](#plano-de-trabalho)
- [Iniciando](#iniciando)
- [Patrocinadores](#patrocinadores)
- [Perguntas frequentes](#perguntas-frequentes)
- [Estrelas ao longo do tempo](#estrelas-ao-longo-do-tempo)
- [Colaboradores](#colaboradores)
- [Licença](#licença)
- [Inspiração](#inspiração)
## Introdução
O método tradicional de fornecer interfaces da Web para programas Go é por meio de um servidor da Web integrado. Wails oferece uma
abordagem: fornece a capacidade de agrupar o código Go e um front-end da Web em um único binário. As ferramentas são fornecidas para
que torne isso mais fácil para você lidando com a criação, compilação e agrupamento de projetos. Tudo o que você precisa fazer é ser criativo!
## Recursos e funcionalidades
- Use Go padrão para o back-end
- Use qualquer tecnologia de front-end com a qual você já esteja familiarizado para criar sua interface do usuário
- Crie rapidamente um front-end avançado para seus programas Go usando modelos pré-construídos
- Chame facilmente métodos Go com JavaScript
- Definições TypeScript geradas automaticamente para suas estruturas e métodos Go
- Diálogos e menus nativos
- Suporte nativo ao modo escuro/claro
- Suporta translucidez moderna e efeitos de "janela fosca"
- Sistema de eventos unificado entre Go e JavaScript
- Poderosa ferramenta cli para gerar e construir rapidamente seus projetos
- Multiplataforma
- Usa mecanismos de renderização nativos - _sem navegador incorporado_!
### Plano de trabalho
O plano de trabalho do projeto pode ser encontrado [aqui](https://github.com/wailsapp/wails/discussions/1484). Por favor consulte
isso antes de abrir um pedido de melhoria.
## Iniciando
As instruções de instalação estão no [site oficial](https://wails.io/docs/gettingstarted/installation).
## Patrocinadores
Este projeto é apoiado por estas simpáticas pessoas/empresas:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## Perguntas frequentes
- Esta é uma alternativa ao Electron?
Depende de seus requisitos. Ele foi projetado para tornar mais fácil para os programadores Go criar aplicações desktop
e adicionar um front-end aos seus aplicativos existentes. O Wails oferece elementos nativos, como menus
e diálogos, por isso pode ser considerada uma alternativa leve, se comparado ao Electron.
- A quem se destina este projeto?
Programadores Go que desejam agrupar um front-end HTML/JS/CSS com seus aplicativos, sem recorrer à criação de um
servidor e abrir um navegador para visualizá-lo.
- Qual é o significado do nome?
Quando vi o WebView, pensei "O que eu realmente quero é ferramentas para construir um aplicativo WebView, algo semelhante ao que Rails é para Ruby". Portanto, inicialmente era um jogo de palavras (WebView on Rails). Por acaso, também era um homófono do
Nome em inglês para o [país](https://en.wikipedia.org/wiki/Wales) de onde eu sou. Então ficou com esse nome.
## Estrelas ao longo do tempo
[![Star History Chart](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
## Colaboradores
A lista de colaboradores está ficando grande demais para o arquivo readme! Todas as pessoas incríveis que contribuíram para o
projeto tem sua própria página [aqui](https://wails.io/credits#contributors).
## Licença
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Inspiração
Este projeto foi construído ouvindo esses álbuns:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -1,153 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Собирайте Desktop приложения используя Go и Web технологии
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## Содержание
- [Содержание](#содержание)
- [Вступление](#вступление)
- [Особенности](#особенности)
- [Roadmap](#roadmap)
- [Быстрый старт](#быстрый-старт)
- [Спонсоры](#спонсоры)
- [FAQ](#faq)
- [График звёздочек](#график-звёздочек-репозитория-относительно-времени)
- [Контребьюторы](#контребьюторы)
- [Лицензия](#лицензия)
- [Вдохновение](#вдохновение)
## Вступление
Обычно, веб-интерфейсы для программ Go - это встроенный веб-сервер и веб-браузер.
У Walls другой подход: он оборачивает как код Go, так и веб-интерфейс в один бинарник (EXE файл).
Облегчает вам создание вашего приложения, управляя созданием, компиляцией и объединением проектов.
Все ограничивается лишь вашей фантазией!
## Особенности
- Использование Go для backend
- Поддержка любой frontend технологии, с которой вы уже знакомы для создания вашего UI
- Быстрое создание frontend для ваших программ, используя готовые шаблоны
- Очень лёгкий вызов функций Go из JavaScript
- Автогенерация TypeScript типов для Go структур и функций
- Нативные диалоги и меню
- Нативная поддержка тёмной и светлой темы
- Поддержка современных эффектов прозрачности и "матового окна"
- Единая система эвентов для Go и JavaScript
- Мощный CLI для быстрого создания ваших проектов
- Мультиплатформенность
- Использование нативного движка рендеринга - нет встроенному браузеру!
### Roadmap
Roadmap проекта вы можете найти [здесь](https://github.com/wailsapp/wails/discussions/1484).
Пожалуйста, проконсультируйтесь перед предложением улучшения.
## Быстрый старт
Инструкции по установке находятся на [официальном сайте](https://wails.io/docs/gettingstarted/installation).
## Спонсоры
Проект поддерживается этими добрыми людьми / компаниями:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## FAQ
- Это альтернатива Electron?
Зависит от ваших требований. Wails разработан для легкого создания Desktop приложений или
расширения интерфейсной части существующих приложений для программистов на Go. Wails действительно
предлагает встроенные элементы, такие как меню и диалоги, так что его можно считать облегченной альтернативой Electron.
- Для кого предназначен этот проект?
Для Golang программистов, которые хотят создавать приложения, используя HTML, JS и CSS,
без создания веб-сервера и открытия браузера для их просмотра.
- Что это за название?
Когда я увидел WebView, я подумал: "Что мне действительно нужно, так это инструменты для создания приложения WebView,
немного похожие на Rails для Ruby". Изначально это была игра слов (Webview on Rails). Просто так получилось, что это
также омофон английского названия для [Страны](https://en.wikipedia.org/wiki/Wales) от куда я родом. Так что это прижилось.
## График звёздочек репозитория по времени
[![График звёзд](https://api.star-history.com/svg?repos=wailsapp/wails&type=Date)](https://star-history.com/#wailsapp/wails&Date)
## Контрибьюторы
Список участников слишком велик для README! У всех замечательных людей, которые внесли свой вклад в этот
проект, есть своя [страничка](https://wails.io/credits#contributors).
## Лицензия
[![Статус FOSSA](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Вдохновение
Этот проект был создан, в основном, под эти альбомы:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -1,156 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Go ve Web Teknolojilerini kullanarak masaüstü uygulamaları oluşturun.
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## İçerik
- [İçerik](#içerik)
- [Giriş](#giriş)
- [Özellikler](#özellikler)
- [Yol Haritası](#yol-haritası)
- [Başlarken](#başlarken)
- [Sponsorlar](#sponsorlar)
- [Sıkça sorulan sorular](#sıkça-sorulan-sorular)
- [Zaman içinda yıldızlayanlar](#zaman-içinde-yıldızlayanlar)
- [Katkıda bulunanlar](#katkıda-bulunanlar)
- [Lisans](#lisans)
- [İlham](#ilham)
## Giriş
Go programlarına web arayüzleri sağlamak için geleneksel yöntem, yerleşik bir web sunucusu kullanmaktır. Wails, farklı bir yaklaşım sunar: Hem Go kodunu hem de bir web ön yüzünü tek bir ikili dosyada paketleme yeteneği sağlar. Proje oluşturma, derleme ve paketleme işlemlerini kolaylaştıran araçlar sunar. Tek yapmanız gereken yaratıcı olmaktır!
## Özellikler
- Backend için standart Go kullanın
- Kullanıcı arayüzünüzü oluşturmak için zaten aşina olduğunuz herhangi bir frontend teknolojisini kullanın
- Hazır şablonlar kullanarak Go programlarınız için hızlıca zengin ön yüzler oluşturun
- Javascript'ten Go metodlarını kolayca çağırın
- Go yapı ve metodlarınız için otomatik oluşturulan Typescript tanımları
- Yerel Diyaloglar ve Menüler
- Yerel Karanlık / Aydınlık mod desteği
- Modern saydamlık ve "buzlu cam" efektlerini destekler
- Go ve Javascript arasında birleşik olay sistemi
- Projelerinizi hızlıca oluşturmak ve derlemek için güçlü bir komut satırı aracı
- Çoklu platform desteği
- Yerel render motorlarını kullanır - _gömülü tarayıcı yok_!
### Yol Haritesı
Proje yol haritasına [buradan](https://github.com/wailsapp/wails/discussions/1484) ulaşabilirsiniz. Lütfen bir iyileştirme talebi oluşturmadan önce danışın.
## Başlarken
Kurulum talimatları [resmi web sitesinde](https://wails.io/docs/gettingstarted/installation) bulunmaktadır.
## Sponsorlar
Bu proje, aşağıdaki nazik insanlar / şirketler tarafından desteklenmektedir:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## Sıkça Sorulan Sorular
- Bu Electron'a alternatif mi?
Gereksinimlerinize bağlıdır. Go programcılarının hafif masaüstü uygulamaları yapmasını veya mevcut uygulamalarına bir ön yüz eklemelerini kolaylaştırmak için tasarlanmıştır. Wails, menüler ve diyaloglar gibi yerel öğeler sunduğundan, hafif bir Electron alternatifi olarak kabul edilebilir.
- Bu proje kimlere yöneliktir?
HTML/JS/CSS ön yüzünü uygulamalarıyla birlikte paketlemek isteyen, ancak bir sunucu oluşturup bir tarayıcı açmaya başvurmadan bunu yapmak isteyen Go programcıları için.
- İsmin anlamı nedir?
WebView'i gördüğümde, "Aslında istediğim şey, WebView uygulaması oluşturmak için araçlar, biraz Rails'in Ruby için olduğu gibi" diye düşündüm. Bu nedenle başlangıçta kelime oyunu (Rails üzerinde Webview) olarak ortaya çıktı. Ayrıca, benim geldiğim [ülkenin](https://en.wikipedia.org/wiki/Wales) İngilizce adıyla homofon olması tesadüf oldu. Bu yüzden bu isim kaldı.
## Zaman içinda yıldızlayanlar
<a href="https://star-history.com/#wailsapp/wails&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
</picture>
</a>
## Katkıda Bulunanlar
Katkıda bulunanların listesi, README için çok büyük hale geldi! Bu projeye katkıda bulunan tüm harika insanların kendi sayfaları [burada](https://wails.io/credits#contributors) bulunmaktadır.
## Lisans
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## İlham
Bu proje esas olarak aşağıdaki albümler dinlenilerek kodlandı:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -1,159 +0,0 @@
<p align="center" style="text-align: center">
<img src="./assets/images/logo-universal.png" width="55%"><br/>
</p>
<p align="center">
Go va Web texnologiyalaridan foydalangan holda ish stoli ilovalarini yarating
<br/>
<br/>
<a href="https://github.com/wailsapp/wails/blob/master/LICENSE">
<img alt="GitHub" src="https://img.shields.io/github/license/wailsapp/wails"/>
</a>
<a href="https://goreportcard.com/report/github.com/wailsapp/wails">
<img src="https://goreportcard.com/badge/github.com/wailsapp/wails" />
</a>
<a href="https://pkg.go.dev/github.com/wailsapp/wails">
<img src="https://pkg.go.dev/badge/github.com/wailsapp/wails.svg" alt="Go Reference"/>
</a>
<a href="https://github.com/wailsapp/wails/issues">
<img src="https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat" alt="CodeFactor" />
</a>
<a href="https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_shield" alt="FOSSA Status">
<img src="https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=shield" />
</a>
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
<img src="https://img.shields.io/github/actions/workflow/status/wailsapp/wails/build-and-test.yml?branch=master&logo=Github" alt="Build" />
</a>
<a href="https://github.com/wailsapp/wails/tags" rel="nofollow">
<img alt="GitHub tag (latest SemVer pre-release)" src="https://img.shields.io/github/v/tag/wailsapp/wails?include_prereleases&label=version"/>
</a>
</p>
<div align="center">
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
</samp>
</strong>
</div>
## Tarkib
- [Tarkib](#tarkib)
- [Kirish](#kirish)
- [Xususiyatlari](#xususiyatlari)
- [Yo'l xaritasi](#yol-xaritasi)
- [Ishni boshlash](#ishni-boshlash)
- [Homiylar](#homiylar)
- [FAQ](#faq)
- [Vaqt o'tishi bilan yulduzlar](#vaqt-otishi-bilan-yulduzlar)
- [Ishtirokchilar](#homiylar)
- [Litsenziya](#litsenziya)
- [Ilhomlanish](#ilhomlanish)
## Kirish
Odatda, Go dasturlari uchun veb-interfeyslar o'rnatilgan veb-server va veb-brauzerdir.
Walls boshqacha yondashuvni qo'llaydi: u Go kodini ham, veb-interfeysni ham bitta ikkilik (e.g: EXE)fayliga o'raydi.
Loyihalarni yaratish, kompilyatsiya qilish va birlashtirishni boshqarish orqali ilovangizni yaratishni osonlashtiradi.
Hamma narsa faqat sizning tasavvuringiz bilan cheklangan!
## Xususiyatlari
- Backend uchun standart Go dan foydalaning
- UI yaratish uchun siz allaqachon tanish bo'lgan har qanday frontend texnologiyasidan foydalaning
- Oldindan tayyorlangan shablonlardan foydalanib, Go dasturlaringiz uchun tezda boy frontendlarni yarating
- Javascriptdan Go methodlarini osongina chaqiring
- Go struktura va methodlari uchun avtomatik yaratilgan Typescript ta'riflari
- Mahalliy Dialoglar va Menyular
- Mahalliy Dark / Light rejimini qo'llab-quvvatlash
- Zamonaviy shaffoflik va "muzli oyna" effektlarini qo'llab-quvvatlaydi
- Go va Javascript o'rtasidagi yagona hodisa tizimi
- Loyihalaringizni tezda yaratish va qurish uchun kuchli cli vositasi
- Ko'p platformali
- Mahalliy renderlash mexanizmlaridan foydalanadi - _o'rnatilgan brauzer yo'q_!
### Yo'l xaritasi
Loyihaning yoʻl xaritasini [bu yerdan](https://github.com/wailsapp/wails/discussions/1484) topish mumkin. Iltimos, maslahatlashing
Buni yaxshilash so'rovini ochishdan oldin.
## Ishni boshlash
O'rnatish bo'yicha ko'rsatmalar [Rasmiy veb saytda](https://wails.io/docs/gettingstarted/installation) mavjud.
## Homiylar
Ushbu loyiha quyidagi mehribon odamlar / kompaniyalar tomonidan qo'llab-quvvatlanadi:
<img src="website/static/img/sponsors.svg" style="width:100%;max-width:800px;"/>
<p align="center">
<img src="https://wails.io/img/sponsor/jetbrains-grayscale.webp" style="width: 100px"/>
</p>
## FAQ
- Bu Elektronga muqobilmi?
Sizning talablaringizga bog'liq. Bu Go dasturchilariga yengil ish stoli yaratishni osonlashtirish uchun yaratilgan
ilovalar yoki ularning mavjud ilovalariga frontend qo'shing. Wails menyular kabi mahalliy elementlarni taklif qiladi
va dialoglar, shuning uchun uni yengil elektron muqobili deb hisoblash mumkin.
- Ushbu loyiha kimlar uchun?
Server yaratmasdan va uni ko'rish uchun brauzerni ochmasdan, o'z ilovalari bilan HTML/JS/CSS orqali frontendini birlashtirmoqchi bo'lgan dasturchilar uchun.
- Bu qanday nom?
Men WebViewni ko'rganimda, men shunday deb o'yladim: "Menga WebView ilovasini yaratish uchun vositalar kerak.
biroz Rails for Rubyga o'xshaydi." Demak, dastlab bu so'zlar ustida o'yin edi (Railsda Webview). Shunday bo'ldi.
u men kelgan [Mamlakat](https://en.wikipedia.org/wiki/Wales)ning inglizcha nomining omofonidir.
## Vaqt o'tishi bilan yulduzlar
<a href="https://star-history.com/#wailsapp/wails&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
<img alt="Yulduzlar tarixi jadvali" src="https://api.star-history.com/svg?repos=wailsapp/wails&type=Date" />
</picture>
</a>
## Ishtirokchilar
Ishtirokchilar roʻyxati oʻqish uchun juda kattalashib bormoqda! Bunga hissa qo'shgan barcha ajoyib odamlarning
loyihada o'z sahifasi bor [bu yerga](https://wails.io/credits#contributors).
## Litsenziya
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fwailsapp%2Fwails.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fwailsapp%2Fwails?ref=badge_large)
## Ilhomlanish
Ushbu loyiha asosan quyidagi albomlar uchun kodlangan:
- [Manic Street Preachers - Resistance Is Futile](https://open.spotify.com/album/1R2rsEUqXjIvAbzM0yHrxA)
- [Manic Street Preachers - This Is My Truth, Tell Me Yours](https://open.spotify.com/album/4VzCL9kjhgGQeKCiojK1YN)
- [The Midnight - Endless Summer](https://open.spotify.com/album/4Krg8zvprquh7TVn9OxZn8)
- [Gary Newman - Savage (Songs from a Broken World)](https://open.spotify.com/album/3kMfsD07Q32HRWKRrpcexr)
- [Steve Vai - Passion & Warfare](https://open.spotify.com/album/0oL0OhrE2rYVns4IGj8h2m)
- [Ben Howard - Every Kingdom](https://open.spotify.com/album/1nJsbWm3Yy2DW1KIc1OKle)
- [Ben Howard - Noonday Dream](https://open.spotify.com/album/6astw05cTiXEc2OvyByaPs)
- [Adwaith - Melyn](https://open.spotify.com/album/2vBE40Rp60tl7rNqIZjaXM)
- [Gwidaith Hen Fran - Cedors Hen Wrach](https://open.spotify.com/album/3v2hrfNGINPLuDP0YDTOjm)
- [Metallica - Metallica](https://open.spotify.com/album/2Kh43m04B1UkVcpcRa1Zug)
- [Bloc Party - Silent Alarm](https://open.spotify.com/album/6SsIdN05HQg2GwYLfXuzLB)
- [Maxthor - Another World](https://open.spotify.com/album/3tklE2Fgw1hCIUstIwPBJF)
- [Alun Tan Lan - Y Distawrwydd](https://open.spotify.com/album/0c32OywcLpdJCWWMC6vB8v)

View file

@ -26,8 +26,8 @@
<a href="https://github.com/avelino/awesome-go" rel="nofollow">
<img src="https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg" alt="Awesome" />
</a>
<a href="https://discord.gg/BrRSWTaxVK">
<img alt="Discord" src="https://img.shields.io/discord/1042734330029547630?logo=discord"/>
<a href="https://app.slack.com/client/T029RQSE6/CJ4P9F7MZ">
<img alt="Slack" src="https://img.shields.io/badge/slack-gophers%2Fwails%20-blue?logo=slack"/>
</a>
<br/>
<a href="https://github.com/wailsapp/wails/actions/workflows/build-and-test.yml" rel="nofollow">
@ -42,10 +42,7 @@
<strong>
<samp>
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) ·
[한국어](README.ko.md) · [Español](README.es.md) · [Português](README.pt-br.md) ·
[Русский](README.ru.md) · [Francais](README.fr.md) · [Uzbek](README.uz.md) · [Deutsch](README.de.md) ·
[Türkçe](README.tr.md)
[English](README.md) · [简体中文](README.zh-Hans.md) · [日本語](README.ja.md) · [한국어](README.ko.md)
</samp>
</strong>
@ -60,7 +57,6 @@
- [快速入门](#快速入门)
- [赞助商](#赞助商)
- [常见问题](#常见问题)
- [星星增长趋势](#星星增长趋势)
- [贡献者](#贡献者)
- [许可证](#许可证)
- [灵感](#灵感)
@ -90,7 +86,7 @@
## 快速入门
使用说明在 [官网](https://wails.io/zh-Hans/docs/gettingstarted/installation/)。
使用说明在 [官网](https://wails.io/docs/gettingstarted/installation)。
## 赞助商

View file

@ -1,38 +0,0 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| 2.x.x | :white_check_mark: |
| 3.0.x-alpha | :x: |
## Reporting a Vulnerability
If you believe you have found a security vulnerability in our project, we encourage you to let us know right away.
We will investigate all legitimate reports and do our best to quickly fix the problem.
Before reporting though, please review our security policy below.
### How to Report
To report a security vulnerability, please use GitHub's [private vulnerability reporting](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability) feature. If possible, please include as much information as possible.
This may include steps to reproduce, impact of the vulnerability, and anything else you believe would help us understand the problem.
**Please do not include any sensitive or personal information in your report**.
### What to Expect
When you report a vulnerability, here's what you can expect:
- **Acknowledgement**: We will acknowledge your email within 48 hours, and you'll receive a more detailed response to your email within 72 hours indicating the next steps in handling your report.
- **Updates**: After the initial reply to your report, our team will keep you informed of the progress being made towards a fix and full announcement. These updates will be sent at least once a week.
- **Confidentiality**: We will maintain strict confidentiality of your report until the security issue is resolved.
- **Issue Resolution**: If the issue is confirmed, we will release a patch as soon as possible depending on complexity of the fix.
- **Recognition**: We recognize and appreciate every individual who helps us identify and fix vulnerabilities in our project. While we do not currently have a bounty program, we would be happy to publicly acknowledge your responsible disclosure.
We strive to make Wails safe for everyone, and we greatly appreciate the assistance of security researchers and users in helping us identify and fix vulnerabilities. Thank you for your contribution to the security of this project.

View file

@ -1,47 +0,0 @@
# https://taskfile.dev
version: "3"
includes:
website:
taskfile: website
dir: website
v2:
taskfile: v2
dir: v2
optional: true
v3:
taskfile: v3
dir: v3
optional: true
tasks:
contributors:check:
cmds:
- npx -y all-contributors-cli check
contributors:update:
cmds:
- go run v3/tasks/contribs/main.go
contributors:build:
cmds:
- npx -y all-contributors-cli generate
format:md:
cmds:
- npx prettier --write "**/*.md"
format:
cmds:
- task: format:md
format-all-md:
cmds:
- task: format:md
- task: website:format:md
- task: v2:format:md
# - task: v2:website:format
- task: v3:format:md
# - task: v3:website:format:md

174
app.go Normal file
View file

@ -0,0 +1,174 @@
package wails
import (
"os"
"syscall"
"github.com/syossan27/tebata"
"github.com/wailsapp/wails/cmd"
"github.com/wailsapp/wails/lib/binding"
"github.com/wailsapp/wails/lib/event"
"github.com/wailsapp/wails/lib/interfaces"
"github.com/wailsapp/wails/lib/ipc"
"github.com/wailsapp/wails/lib/logger"
"github.com/wailsapp/wails/lib/renderer"
wailsruntime "github.com/wailsapp/wails/runtime"
)
// -------------------------------- Compile time Flags ------------------------------
// BuildMode indicates what mode we are in
var BuildMode = cmd.BuildModeProd
// Runtime is the Go Runtime struct
type Runtime = wailsruntime.Runtime
// Store is a state store used for syncing with
// the front end
type Store = wailsruntime.Store
// CustomLogger is a specialised logger
type CustomLogger = logger.CustomLogger
// ----------------------------------------------------------------------------------
// App defines the main application struct
type App struct {
config *AppConfig // The Application configuration object
cli *cmd.Cli // In debug mode, we have a cli
renderer interfaces.Renderer // The renderer is what we will render the app to
logLevel string // The log level of the app
ipc interfaces.IPCManager // Handles the IPC calls
log *logger.CustomLogger // Logger
bindingManager interfaces.BindingManager // Handles binding of Go code to renderer
eventManager interfaces.EventManager // Handles all the events
runtime interfaces.Runtime // The runtime object for registered structs
}
// CreateApp creates the application window with the given configuration
// If none given, the defaults are used
func CreateApp(optionalConfig ...*AppConfig) *App {
var userConfig *AppConfig
if len(optionalConfig) > 0 {
userConfig = optionalConfig[0]
}
result := &App{
logLevel: "debug",
renderer: renderer.NewWebView(),
ipc: ipc.NewManager(),
bindingManager: binding.NewManager(),
eventManager: event.NewManager(),
log: logger.NewCustomLogger("App"),
}
appconfig, err := newConfig(userConfig)
if err != nil {
result.log.Fatalf("Cannot use custom HTML: %s", err.Error())
}
result.config = appconfig
// Set up the CLI if not in release mode
if BuildMode != cmd.BuildModeProd {
result.cli = result.setupCli()
} else {
// Disable Inspector in release mode
result.config.DisableInspector = true
}
// Platform specific init
platformInit()
return result
}
// Run the app
func (a *App) Run() error {
if BuildMode != cmd.BuildModeProd {
return a.cli.Run()
}
a.logLevel = "error"
err := a.start()
if err != nil {
a.log.Error(err.Error())
}
return err
}
func (a *App) start() error {
// Set the log level
logger.SetLogLevel(a.logLevel)
// Log starup
a.log.Info("Starting")
// Check if we are to run in bridge mode
if BuildMode == cmd.BuildModeBridge {
a.renderer = renderer.NewBridge()
}
// Initialise the renderer
err := a.renderer.Initialise(a.config, a.ipc, a.eventManager)
if err != nil {
return err
}
// Start signal handler
t := tebata.New(os.Interrupt, os.Kill, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
t.Reserve(func() {
a.log.Debug("SIGNAL CAUGHT! Starting Shutdown")
a.renderer.Close()
})
// Start event manager and give it our renderer
a.eventManager.Start(a.renderer)
// Start the IPC Manager and give it the event manager and binding manager
a.ipc.Start(a.eventManager, a.bindingManager)
// Create the runtime
a.runtime = wailsruntime.NewRuntime(a.eventManager, a.renderer)
// Start binding manager and give it our renderer
err = a.bindingManager.Start(a.renderer, a.runtime)
if err != nil {
return err
}
// Defer the shutdown
defer a.shutdown()
// run the renderer
err = a.renderer.Run()
if err != nil {
return err
}
return nil
}
// shutdown the app
func (a *App) shutdown() {
// Make sure this is only called once
a.log.Debug("Shutting down")
// Shutdown Binding Manager
a.bindingManager.Shutdown()
// Shutdown IPC Manager
a.ipc.Shutdown()
// Shutdown Event Manager
a.eventManager.Shutdown()
a.log.Debug("Cleanly Shutdown")
}
// Bind allows the user to bind the given object
// with the application
func (a *App) Bind(object interface{}) {
a.bindingManager.Bind(object)
}

8
app_other.go Normal file
View file

@ -0,0 +1,8 @@
//go:build linux || darwin || !windows
// +build linux darwin !windows
package wails
func platformInit() {
}

28
app_windows.go Normal file
View file

@ -0,0 +1,28 @@
//go:build windows || !linux || !darwin
// +build windows !linux !darwin
package wails
import (
"fmt"
"log"
"syscall"
)
func platformInit() {
err := SetProcessDPIAware()
if err != nil {
log.Fatalf(err.Error())
}
}
// SetProcessDPIAware via user32.dll
// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware
// Also, thanks Jack Mordaunt! https://github.com/wailsapp/wails/issues/293
func SetProcessDPIAware() error {
status, r, err := syscall.NewLazyDLL("user32.dll").NewProc("SetProcessDPIAware").Call()
if status == 0 {
return fmt.Errorf("exit status %d: %v %v", status, r, err)
}
return nil
}

27
cli.go Normal file
View file

@ -0,0 +1,27 @@
package wails
import (
"github.com/wailsapp/wails/cmd"
)
// setupCli creates a new cli handler for the application
func (app *App) setupCli() *cmd.Cli {
// Create a new cli
result := cmd.NewCli(app.config.Title, "Debug build")
result.Version(cmd.Version)
// Setup cli to handle loglevel
result.
StringFlag("loglevel", "Sets the log level [debug|info|error|panic|fatal]. Default debug", &app.logLevel).
Action(app.start)
// Banner
result.PreRun(func(cli *cmd.Cli) error {
log := cmd.NewLogger()
log.YellowUnderline(app.config.Title + " - Debug Build")
return nil
})
return result
}

10
cmd/build.go Normal file
View file

@ -0,0 +1,10 @@
package cmd
const (
// BuildModeProd indicates we are building for prod mode
BuildModeProd = "prod"
// BuildModeDebug indicates we are building for debug mode
BuildModeDebug = "debug"
// BuildModeBridge indicates we are building for bridge mode
BuildModeBridge = "bridge"
)

285
cmd/cli.go Normal file
View file

@ -0,0 +1,285 @@
package cmd
import (
"flag"
"fmt"
"os"
"strings"
)
// NewCli - Creates a new Cli application object
func NewCli(name, description string) *Cli {
result := &Cli{}
result.rootCommand = NewCommand(name, description, result, "")
result.log = NewLogger()
return result
}
// Cli - The main application object
type Cli struct {
rootCommand *Command
defaultCommand *Command
preRunCommand func(*Cli) error
log *Logger
}
// Version - Set the Application version string
func (c *Cli) Version(version string) {
c.rootCommand.AppVersion = version
}
// PrintHelp - Prints the application's help
func (c *Cli) PrintHelp() {
c.rootCommand.PrintHelp()
}
// Run - Runs the application with the given arguments
func (c *Cli) Run(args ...string) error {
if c.preRunCommand != nil {
err := c.preRunCommand(c)
if err != nil {
return err
}
}
if len(args) == 0 {
args = os.Args[1:]
}
return c.rootCommand.Run(args)
}
// DefaultCommand - Sets the given command as the command to run when
// no other commands given
func (c *Cli) DefaultCommand(defaultCommand *Command) *Cli {
c.defaultCommand = defaultCommand
return c
}
// Command - Adds a command to the application
func (c *Cli) Command(name, description string) *Command {
return c.rootCommand.Command(name, description)
}
// PreRun - Calls the given function before running the specific command
func (c *Cli) PreRun(callback func(*Cli) error) {
c.preRunCommand = callback
}
// BoolFlag - Adds a boolean flag to the root command
func (c *Cli) BoolFlag(name, description string, variable *bool) *Command {
c.rootCommand.BoolFlag(name, description, variable)
return c.rootCommand
}
// StringFlag - Adds a string flag to the root command
func (c *Cli) StringFlag(name, description string, variable *string) *Command {
c.rootCommand.StringFlag(name, description, variable)
return c.rootCommand
}
// Action represents a function that gets calls when the command is called by
// the user
type Action func() error
// Command represents a command that may be run by the user
type Command struct {
Name string
CommandPath string
Shortdescription string
Longdescription string
AppVersion string
SubCommands []*Command
SubCommandsMap map[string]*Command
longestSubcommand int
ActionCallback Action
App *Cli
Flags *flag.FlagSet
flagCount int
log *Logger
helpFlag bool
hidden bool
}
// NewCommand creates a new Command
func NewCommand(name string, description string, app *Cli, parentCommandPath string) *Command {
result := &Command{
Name: name,
Shortdescription: description,
SubCommandsMap: make(map[string]*Command),
App: app,
log: NewLogger(),
hidden: false,
}
// Set up command path
if parentCommandPath != "" {
result.CommandPath += parentCommandPath + " "
}
result.CommandPath += name
// Set up flag set
result.Flags = flag.NewFlagSet(result.CommandPath, flag.ContinueOnError)
result.BoolFlag("help", "Get help on the '"+result.CommandPath+"' command.", &result.helpFlag)
// result.Flags.Usage = result.PrintHelp
return result
}
// parseFlags parses the given flags
func (c *Command) parseFlags(args []string) error {
// Parse flags
tmp := os.Stderr
os.Stderr = nil
err := c.Flags.Parse(args)
os.Stderr = tmp
if err != nil {
fmt.Printf("Error: %s\n\n", err.Error())
c.PrintHelp()
}
return err
}
// Run - Runs the Command with the given arguments
func (c *Command) Run(args []string) error {
// If we have arguments, process them
if len(args) > 0 {
// Check for subcommand
subcommand := c.SubCommandsMap[args[0]]
if subcommand != nil {
return subcommand.Run(args[1:])
}
// Parse flags
err := c.parseFlags(args)
if err != nil {
fmt.Printf("Error: %s\n\n", err.Error())
c.PrintHelp()
return err
}
// Help takes precedence
if c.helpFlag {
c.PrintHelp()
return nil
}
}
// Do we have an action?
if c.ActionCallback != nil {
return c.ActionCallback()
}
// If we haven't specified a subcommand
// check for an app level default command
if c.App.defaultCommand != nil {
// Prevent recursion!
if c.App.defaultCommand != c {
// only run default command if no args passed
if len(args) == 0 {
return c.App.defaultCommand.Run(args)
}
}
}
// Nothing left we can do
c.PrintHelp()
return nil
}
// Action - Define an action from this command
func (c *Command) Action(callback Action) *Command {
c.ActionCallback = callback
return c
}
// PrintHelp - Output the help text for this command
func (c *Command) PrintHelp() {
c.log.PrintBanner()
commandTitle := c.CommandPath
if c.Shortdescription != "" {
commandTitle += " - " + c.Shortdescription
}
// Ignore root command
if c.CommandPath != c.Name {
c.log.Yellow(commandTitle)
}
if c.Longdescription != "" {
fmt.Println()
fmt.Println(c.Longdescription + "\n")
}
if len(c.SubCommands) > 0 {
c.log.White("Available commands:")
fmt.Println("")
for _, subcommand := range c.SubCommands {
if subcommand.isHidden() {
continue
}
spacer := strings.Repeat(" ", 3+c.longestSubcommand-len(subcommand.Name))
isDefault := ""
if subcommand.isDefaultCommand() {
isDefault = "[default]"
}
fmt.Printf(" %s%s%s %s\n", subcommand.Name, spacer, subcommand.Shortdescription, isDefault)
}
fmt.Println("")
}
if c.flagCount > 0 {
c.log.White("Flags:")
fmt.Println()
c.Flags.SetOutput(os.Stdout)
c.Flags.PrintDefaults()
c.Flags.SetOutput(os.Stderr)
}
fmt.Println()
}
// isDefaultCommand returns true if called on the default command
func (c *Command) isDefaultCommand() bool {
return c.App.defaultCommand == c
}
// isHidden returns true if the command is a hidden command
func (c *Command) isHidden() bool {
return c.hidden
}
// Hidden hides the command from the Help system
func (c *Command) Hidden() {
c.hidden = true
}
// Command - Defines a subcommand
func (c *Command) Command(name, description string) *Command {
result := NewCommand(name, description, c.App, c.CommandPath)
result.log = c.log
c.SubCommands = append(c.SubCommands, result)
c.SubCommandsMap[name] = result
if len(name) > c.longestSubcommand {
c.longestSubcommand = len(name)
}
return result
}
// BoolFlag - Adds a boolean flag to the command
func (c *Command) BoolFlag(name, description string, variable *bool) *Command {
c.Flags.BoolVar(variable, name, *variable, description)
c.flagCount++
return c
}
// StringFlag - Adds a string flag to the command
func (c *Command) StringFlag(name, description string, variable *string) *Command {
c.Flags.StringVar(variable, name, *variable, description)
c.flagCount++
return c
}
// LongDescription - Sets the long description for the command
func (c *Command) LongDescription(Longdescription string) *Command {
c.Longdescription = Longdescription
return c
}

240
cmd/fs.go Normal file
View file

@ -0,0 +1,240 @@
package cmd
import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt"
"io"
"log"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/leaanthony/slicer"
)
// FSHelper - Wrapper struct for File System utility commands
type FSHelper struct {
}
// NewFSHelper - Returns a new FSHelper
func NewFSHelper() *FSHelper {
result := &FSHelper{}
return result
}
// DirExists - Returns true if the given path resolves to a directory on the filesystem
func (fs *FSHelper) DirExists(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.Mode().IsDir()
}
// FileExists returns a boolean value indicating whether
// the given file exists
func (fs *FSHelper) FileExists(path string) bool {
fi, err := os.Lstat(path)
if err != nil {
return false
}
return fi.Mode().IsRegular()
}
// FindFile returns the first occurrence of match inside path.
func (fs *FSHelper) FindFile(path, match string) (string, error) {
files, err := os.ReadDir(path)
if err != nil {
return "", err
}
for _, f := range files {
if !f.IsDir() && strings.Contains(f.Name(), match) {
return f.Name(), nil
}
}
return "", fmt.Errorf("file not found")
}
// CreateFile creates a file at the given filename location with the contents
// set to the given data. It will create intermediary directories if needed.
func (fs *FSHelper) CreateFile(filename string, data []byte) error {
// Ensure directory exists
fs.MkDirs(filepath.Dir(filename))
return os.WriteFile(filename, data, 0644)
}
// MkDirs creates the given nested directories.
// Returns error on failure
func (fs *FSHelper) MkDirs(fullPath string, mode ...os.FileMode) error {
var perms os.FileMode
perms = 0700
if len(mode) == 1 {
perms = mode[0]
}
return os.MkdirAll(fullPath, perms)
}
// CopyFile from source to target
func (fs *FSHelper) CopyFile(source, target string) error {
s, err := os.Open(source)
if err != nil {
return err
}
defer s.Close()
d, err := os.Create(target)
if err != nil {
return err
}
if _, err := io.Copy(d, s); err != nil {
d.Close()
return err
}
return d.Close()
}
// Cwd returns the current working directory
// Aborts on Failure
func (fs *FSHelper) Cwd() string {
cwd, err := os.Getwd()
if err != nil {
log.Fatal("Unable to get working directory!")
}
return cwd
}
// RemoveFile removes the given filename
func (fs *FSHelper) RemoveFile(filename string) error {
return os.Remove(filename)
}
// RemoveFiles removes the given filenames
func (fs *FSHelper) RemoveFiles(files []string, continueOnError bool) error {
for _, filename := range files {
err := os.Remove(filename)
if err != nil && !continueOnError {
return err
}
}
return nil
}
// Dir holds information about a directory
type Dir struct {
localPath string
fullPath string
}
// Directory creates a new Dir struct with the given directory path
func (fs *FSHelper) Directory(dir string) (*Dir, error) {
fullPath, err := filepath.Abs(dir)
return &Dir{fullPath: fullPath}, err
}
// LocalDir creates a new Dir struct based on a path relative to the caller
func (fs *FSHelper) LocalDir(dir string) (*Dir, error) {
_, filename, _, _ := runtime.Caller(1)
fullPath, err := filepath.Abs(filepath.Join(path.Dir(filename), dir))
return &Dir{
localPath: dir,
fullPath: fullPath,
}, err
}
// GetSubdirs will return a list of FQPs to subdirectories in the given directory
func (d *Dir) GetSubdirs() (map[string]string, error) {
// Read in the directory information
fileInfo, err := os.ReadDir(d.fullPath)
if err != nil {
return nil, err
}
// Allocate space for the list
subdirs := make(map[string]string)
// Pull out the directories and store in the map as
// map["directoryName"] = "path/to/directoryName"
for _, file := range fileInfo {
if file.IsDir() {
subdirs[file.Name()] = filepath.Join(d.fullPath, file.Name())
}
}
return subdirs, nil
}
// GetAllFilenames returns all filename in and below this directory
func (d *Dir) GetAllFilenames() (*slicer.StringSlicer, error) {
result := slicer.String()
err := filepath.Walk(d.fullPath, func(dir string, info os.FileInfo, err error) error {
if dir == d.fullPath {
return nil
}
if err != nil {
return err
}
// Don't copy template metadata
result.Add(dir)
return nil
})
return result, err
}
// MkDir creates the given directory.
// Returns error on failure
func (fs *FSHelper) MkDir(dir string) error {
return os.Mkdir(dir, 0700)
}
// SaveAsJSON saves the JSON representation of the given data to the given filename
func (fs *FSHelper) SaveAsJSON(data interface{}, filename string) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.SetEscapeHTML(false)
e.SetIndent("", " ")
e.Encode(data)
err := os.WriteFile(filename, buf.Bytes(), 0755)
if err != nil {
return err
}
return nil
}
// LoadAsString will attempt to load the given file and return
// its contents as a string
func (fs *FSHelper) LoadAsString(filename string) (string, error) {
bytes, err := fs.LoadAsBytes(filename)
return string(bytes), err
}
// LoadAsBytes returns the contents of the file as a byte slice
func (fs *FSHelper) LoadAsBytes(filename string) ([]byte, error) {
return os.ReadFile(filename)
}
// FileMD5 returns the md5sum of the given file
func (fs *FSHelper) FileMD5(filename string) (string, error) {
f, err := os.Open(filename)
if err != nil {
return "", err
}
defer f.Close()
h := md5.New()
if _, err := io.Copy(h, f); err != nil {
return "", err
}
return fmt.Sprintf("%x", h.Sum(nil)), nil
}

108
cmd/github.go Normal file
View file

@ -0,0 +1,108 @@
package cmd
import (
"encoding/json"
"fmt"
"io"
"net/http"
"sort"
)
// GitHubHelper is a utility class for interacting with GitHub
type GitHubHelper struct {
}
// NewGitHubHelper returns a new GitHub Helper
func NewGitHubHelper() *GitHubHelper {
return &GitHubHelper{}
}
// GetVersionTags gets the list of tags on the Wails repo
// It returns a list of sorted tags in descending order
func (g *GitHubHelper) GetVersionTags() ([]*SemanticVersion, error) {
result := []*SemanticVersion{}
var err error
resp, err := http.Get("https://api.github.com/repos/wailsapp/wails/releases")
if err != nil {
return result, err
}
body, err := io.ReadAll(resp.Body)
if err != nil {
return result, err
}
data := []map[string]interface{}{}
err = json.Unmarshal(body, &data)
if err != nil {
return result, err
}
// Convert tag data to Version structs
for _, tag := range data {
version := tag["name"].(string)
semver, err := NewSemanticVersion(version)
if err != nil {
return result, err
}
result = append(result, semver)
}
// Reverse Sort
sort.Sort(sort.Reverse(SemverCollection(result)))
return result, err
}
// GetLatestStableRelease gets the latest stable release on GitHub
func (g *GitHubHelper) GetLatestStableRelease() (result *SemanticVersion, err error) {
tags, err := g.GetVersionTags()
if err != nil {
return nil, err
}
for _, tag := range tags {
if tag.IsRelease() {
return tag, nil
}
}
return nil, fmt.Errorf("no release tag found")
}
// GetLatestPreRelease gets the latest prerelease on GitHub
func (g *GitHubHelper) GetLatestPreRelease() (result *SemanticVersion, err error) {
tags, err := g.GetVersionTags()
if err != nil {
return nil, err
}
for _, tag := range tags {
if tag.IsPreRelease() {
return tag, nil
}
}
return nil, fmt.Errorf("no prerelease tag found")
}
// IsValidTag returns true if the given string is a valid tag
func (g *GitHubHelper) IsValidTag(tagVersion string) (bool, error) {
if tagVersion[0] == 'v' {
tagVersion = tagVersion[1:]
}
tags, err := g.GetVersionTags()
if err != nil {
return false, err
}
for _, tag := range tags {
if tag.String() == tagVersion {
return true, nil
}
}
return false, nil
}

78
cmd/gomod.go Normal file
View file

@ -0,0 +1,78 @@
package cmd
import (
"fmt"
"path/filepath"
"regexp"
"github.com/Masterminds/semver"
)
func GetWailsVersion() (*semver.Version, error) {
var FS = NewFSHelper()
var result *semver.Version
// Load file
var err error
goModFile, err := filepath.Abs(filepath.Join(".", "go.mod"))
if err != nil {
return nil, fmt.Errorf("Unable to load go.mod at %s", goModFile)
}
goMod, err := FS.LoadAsString(goModFile)
if err != nil {
return nil, fmt.Errorf("Unable to load go.mod")
}
// Find wails version
versionRegexp := regexp.MustCompile(`.*github.com/wailsapp/wails.*(v\d+.\d+.\d+(?:-pre\d+)?)`)
versions := versionRegexp.FindStringSubmatch(goMod)
if len(versions) != 2 {
return nil, fmt.Errorf("Unable to determine Wails version")
}
version := versions[1]
result, err = semver.NewVersion(version)
if err != nil {
return nil, fmt.Errorf("Unable to parse Wails version: %s", version)
}
return result, nil
}
func GetCurrentVersion() (*semver.Version, error) {
result, err := semver.NewVersion(Version)
if err != nil {
return nil, fmt.Errorf("Unable to parse Wails version: %s", Version)
}
return result, nil
}
func GoModOutOfSync() (bool, error) {
gomodversion, err := GetWailsVersion()
if err != nil {
return true, err
}
currentVersion, err := GetCurrentVersion()
if err != nil {
return true, err
}
result := !currentVersion.Equal(gomodversion)
return result, nil
}
func UpdateGoModVersion() error {
currentVersion, err := GetCurrentVersion()
if err != nil {
return err
}
currentVersionString := currentVersion.String()
requireLine := "-require=github.com/wailsapp/wails@v" + currentVersionString
// Issue: go mod edit -require=github.com/wailsapp/wails@1.0.2-pre5
helper := NewProgramHelper()
command := []string{"go", "mod", "edit", requireLine}
return helper.RunCommandArray(command)
}

542
cmd/helpers.go Normal file
View file

@ -0,0 +1,542 @@
package cmd
import (
"fmt"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/leaanthony/slicer"
"github.com/leaanthony/spinner"
wailsruntime "github.com/wailsapp/wails/runtime"
)
const xgoVersion = "1.16.3"
var fs = NewFSHelper()
// ValidateFrontendConfig checks if the frontend config is valid
func ValidateFrontendConfig(projectOptions *ProjectOptions) error {
if projectOptions.FrontEnd.Dir == "" {
return fmt.Errorf("Frontend directory not set in project.json")
}
if projectOptions.FrontEnd.Build == "" {
return fmt.Errorf("Frontend build command not set in project.json")
}
if projectOptions.FrontEnd.Install == "" {
return fmt.Errorf("Frontend install command not set in project.json")
}
if projectOptions.FrontEnd.Bridge == "" {
return fmt.Errorf("Frontend bridge config not set in project.json")
}
return nil
}
// InstallGoDependencies will run go get in the current directory
func InstallGoDependencies(verbose bool) error {
var depSpinner *spinner.Spinner
if !verbose {
depSpinner = spinner.New("Ensuring Dependencies are up to date...")
depSpinner.SetSpinSpeed(50)
depSpinner.Start()
}
err := NewProgramHelper(verbose).RunCommand("go mod tidy")
if err != nil {
if !verbose {
depSpinner.Error()
}
return err
}
if !verbose {
depSpinner.Success()
}
return nil
}
func InitializeCrossCompilation(verbose bool) error {
// Check Docker
if err := CheckIfInstalled("docker"); err != nil {
return err
}
var packSpinner *spinner.Spinner
msg := fmt.Sprintf("Pulling wailsapp/xgo:%s docker image... (may take a while)", xgoVersion)
if !verbose {
packSpinner = spinner.New(msg)
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(msg)
}
err := NewProgramHelper(verbose).RunCommandArray([]string{"docker",
"pull", fmt.Sprintf("wailsapp/xgo:%s", xgoVersion)})
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildDocker builds the project using the cross compiling wailsapp/xgo:<xgoVersion> container
func BuildDocker(binaryName string, buildMode string, projectOptions *ProjectOptions) error {
var packSpinner *spinner.Spinner
if buildMode == BuildModeBridge {
return fmt.Errorf("you cant serve the application in cross-compilation")
}
// Check build directory
buildDirectory := filepath.Join(fs.Cwd(), "build")
if !fs.DirExists(buildDirectory) {
err := fs.MkDir(buildDirectory)
if err != nil {
return err
}
}
buildCommand := slicer.String()
userid := 1000
currentUser, _ := user.Current()
if i, err := strconv.Atoi(currentUser.Uid); err == nil {
userid = i
}
for _, arg := range []string{
"docker",
"run",
"--rm",
"-v", fmt.Sprintf("%s:/build", filepath.Join(fs.Cwd(), "build")),
"-v", fmt.Sprintf("%s:/source", fs.Cwd()),
"-e", fmt.Sprintf("LOCAL_USER_ID=%v", userid),
"-e", fmt.Sprintf("FLAG_TAGS=%s", projectOptions.Tags),
"-e", fmt.Sprintf("FLAG_LDFLAGS=%s", ldFlags(projectOptions, buildMode)),
"-e", "FLAG_V=false",
"-e", "FLAG_X=false",
"-e", "FLAG_RACE=false",
"-e", "FLAG_BUILDMODE=default",
"-e", "FLAG_TRIMPATH=false",
"-e", fmt.Sprintf("TARGETS=%s/%s", projectOptions.Platform, projectOptions.Architecture),
"-e", "GOPROXY=",
"-e", "GO111MODULE=on",
} {
buildCommand.Add(arg)
}
if projectOptions.GoPath != "" {
buildCommand.Add("-v")
buildCommand.Add(fmt.Sprintf("%s:/go", projectOptions.GoPath))
}
buildCommand.Add(fmt.Sprintf("wailsapp/xgo:%s", xgoVersion))
buildCommand.Add(".")
compileMessage := fmt.Sprintf(
"Packing + Compiling project for %s/%s using docker image wailsapp/xgo:%s",
projectOptions.Platform, projectOptions.Architecture, xgoVersion)
if buildMode == BuildModeDebug {
compileMessage += " (Debug Mode)"
}
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildNative builds on the target platform itself.
func BuildNative(binaryName string, forceRebuild bool, buildMode string, projectOptions *ProjectOptions) error {
if err := CheckWindres(); err != nil {
return err
}
compileMessage := "Packing + Compiling project"
if buildMode == BuildModeDebug {
compileMessage += " (Debug Mode)"
}
var packSpinner *spinner.Spinner
if !projectOptions.Verbose {
packSpinner = spinner.New(compileMessage + "...")
packSpinner.SetSpinSpeed(50)
packSpinner.Start()
} else {
println(compileMessage)
}
buildCommand := slicer.String()
buildCommand.Add("go")
buildCommand.Add("build")
if binaryName != "" {
// Alter binary name based on OS
switch projectOptions.Platform {
case "windows":
if !strings.HasSuffix(binaryName, ".exe") {
binaryName += ".exe"
}
default:
if strings.HasSuffix(binaryName, ".exe") {
binaryName = strings.TrimSuffix(binaryName, ".exe")
}
}
buildCommand.Add("-o", filepath.Join("build", binaryName))
}
// If we are forcing a rebuild
if forceRebuild {
buildCommand.Add("-a")
}
buildCommand.AddSlice([]string{"-ldflags", ldFlags(projectOptions, buildMode)})
if projectOptions.Tags != "" {
buildCommand.AddSlice([]string{"--tags", projectOptions.Tags})
}
if projectOptions.Verbose {
fmt.Printf("Command: %v\n", buildCommand.AsSlice())
}
err := NewProgramHelper(projectOptions.Verbose).RunCommandArray(buildCommand.AsSlice())
if err != nil {
if packSpinner != nil {
packSpinner.Error()
}
return err
}
if packSpinner != nil {
packSpinner.Success()
}
return nil
}
// BuildApplication will attempt to build the project based on the given inputs
func BuildApplication(binaryName string, forceRebuild bool, buildMode string, packageApp bool, projectOptions *ProjectOptions) error {
var err error
if projectOptions.CrossCompile {
if err := InitializeCrossCompilation(projectOptions.Verbose); err != nil {
return err
}
}
helper := NewPackageHelper(projectOptions.Platform)
// Generate windows resources
if projectOptions.Platform == "windows" {
if err := helper.PackageWindows(projectOptions, false); err != nil {
return err
}
}
if projectOptions.CrossCompile {
err = BuildDocker(binaryName, buildMode, projectOptions)
} else {
err = BuildNative(binaryName, forceRebuild, buildMode, projectOptions)
}
if err != nil {
return err
}
if packageApp {
err = PackageApplication(projectOptions)
if err != nil {
return err
}
}
return nil
}
// PackageApplication will attempt to package the application in a platform dependent way
func PackageApplication(projectOptions *ProjectOptions) error {
var packageSpinner *spinner.Spinner
if projectOptions.Verbose {
packageSpinner = spinner.New("Packaging application...")
packageSpinner.SetSpinSpeed(50)
packageSpinner.Start()
}
err := NewPackageHelper(projectOptions.Platform).Package(projectOptions)
if err != nil {
if packageSpinner != nil {
packageSpinner.Error()
}
return err
}
if packageSpinner != nil {
packageSpinner.Success()
}
return nil
}
// BuildFrontend runs the given build command
func BuildFrontend(projectOptions *ProjectOptions) error {
var buildFESpinner *spinner.Spinner
if !projectOptions.Verbose {
buildFESpinner = spinner.New("Building frontend...")
buildFESpinner.SetSpinSpeed(50)
buildFESpinner.Start()
} else {
println("Building frontend...")
}
err := NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Build)
if err != nil {
if buildFESpinner != nil {
buildFESpinner.Error()
}
return err
}
if buildFESpinner != nil {
buildFESpinner.Success()
}
return nil
}
// CheckWindres checks if Windres is installed and if not, aborts
func CheckWindres() (err error) {
if runtime.GOOS != "windows" { // FIXME: Handle windows cross-compile for windows!
return nil
}
programHelper := NewProgramHelper()
if !programHelper.IsInstalled("windres") {
return fmt.Errorf("windres not installed. It comes by default with mingw. Ensure you have installed mingw correctly")
}
return nil
}
// CheckIfInstalled returns if application is installed
func CheckIfInstalled(application string) (err error) {
programHelper := NewProgramHelper()
if !programHelper.IsInstalled(application) {
return fmt.Errorf("%s not installed. Ensure you have installed %s correctly", application, application)
}
return nil
}
// InstallFrontendDeps attempts to install the frontend dependencies based on the given options
func InstallFrontendDeps(projectDir string, projectOptions *ProjectOptions, forceRebuild bool, caller string) error {
// Install frontend deps
err := os.Chdir(projectOptions.FrontEnd.Dir)
if err != nil {
return err
}
// Check if frontend deps have been updated
var feSpinner *spinner.Spinner
if !projectOptions.Verbose {
feSpinner = spinner.New("Ensuring frontend dependencies are up to date (This may take a while)")
feSpinner.SetSpinSpeed(50)
feSpinner.Start()
} else {
println("Ensuring frontend dependencies are up to date (This may take a while)")
}
requiresNPMInstall := true
// Read in package.json MD5
fs := NewFSHelper()
packageJSONMD5, err := fs.FileMD5("package.json")
if err != nil {
return err
}
const md5sumFile = "package.json.md5"
// If node_modules does not exist, force a rebuild.
nodeModulesPath, err := filepath.Abs(filepath.Join(".", "node_modules"))
if err != nil {
return err
}
if !fs.DirExists(nodeModulesPath) {
forceRebuild = true
}
// If we aren't forcing the install and the md5sum file exists
if !forceRebuild && fs.FileExists(md5sumFile) {
// Yes - read contents
savedMD5sum, err := fs.LoadAsString(md5sumFile)
// File exists
if err == nil {
// Compare md5
if savedMD5sum == packageJSONMD5 {
// Same - no need for reinstall
requiresNPMInstall = false
if feSpinner != nil {
feSpinner.Success("Skipped frontend dependencies (-f to force rebuild)")
} else {
println("Skipped frontend dependencies (-f to force rebuild)")
}
}
}
}
// Md5 sum package.json
// Different? Build
if requiresNPMInstall || forceRebuild {
// Install dependencies
err = NewProgramHelper(projectOptions.Verbose).RunCommand(projectOptions.FrontEnd.Install)
if err != nil {
if feSpinner != nil {
feSpinner.Error()
}
return err
}
if feSpinner != nil {
feSpinner.Success()
}
// Update md5sum file
err := os.WriteFile(md5sumFile, []byte(packageJSONMD5), 0644)
if err != nil {
return err
}
}
// Install the runtime
if caller == "build" {
err = InstallProdRuntime(projectDir, projectOptions)
} else {
err = InstallBridge(projectDir, projectOptions)
}
if err != nil {
return err
}
// Build frontend
err = BuildFrontend(projectOptions)
if err != nil {
return err
}
return nil
}
// InstallBridge installs the relevant bridge javascript library
func InstallBridge(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, wailsruntime.BridgeJS)
return err
}
// InstallProdRuntime installs the production runtime
func InstallProdRuntime(projectDir string, projectOptions *ProjectOptions) error {
bridgeFileTarget := filepath.Join(projectDir, projectOptions.FrontEnd.Dir, "node_modules", "@wailsapp", "runtime", "init.js")
err := fs.CreateFile(bridgeFileTarget, wailsruntime.InitJS)
return err
}
// ServeProject attempts to serve up the current project so that it may be connected to
// via the Wails bridge
func ServeProject(projectOptions *ProjectOptions, logger *Logger) error {
go func() {
time.Sleep(2 * time.Second)
if projectOptions.Platform == "windows" {
logger.Yellow("*** Please note: Windows builds use mshtml which is only compatible with IE11. We strongly recommend only using IE11 when running 'wails serve'! For more information, please read https://wails.app/guides/windows/ ***")
}
logger.Green(">>>>> To connect, you will need to run '" + projectOptions.FrontEnd.Serve + "' in the '" + projectOptions.FrontEnd.Dir + "' directory <<<<<")
}()
location, err := filepath.Abs(filepath.Join("build", projectOptions.BinaryName))
if err != nil {
return err
}
logger.Yellow("Serving Application: " + location)
var args []string
if len(os.Args) > 2 {
foundArgSep := false
for index, arg := range os.Args[2:] {
if arg == "--" {
foundArgSep = true
continue
}
if foundArgSep {
args = os.Args[index:]
break
}
}
logger.Yellow("Passing arguments: %+v", args)
}
cmd := exec.Command(location, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
if err != nil {
return err
}
return nil
}
func ldFlags(po *ProjectOptions, buildMode string) string {
// Setup ld flags
ldflags := "-w -s "
if buildMode == BuildModeDebug {
ldflags = ""
}
// Add windows flags
if po.Platform == "windows" && buildMode == BuildModeProd {
ldflags += "-H windowsgui "
}
if po.UseFirebug {
ldflags += "-X github.com/wailsapp/wails/lib/renderer.UseFirebug=true "
}
ldflags += "-X github.com/wailsapp/wails.BuildMode=" + buildMode
// Add additional ldflags passed in via the `ldflags` cli flag
if len(po.LdFlags) > 0 {
ldflags += " " + po.LdFlags
}
// If we wish to generate typescript
if po.typescriptDefsFilename != "" {
cwd, err := os.Getwd()
if err == nil {
filename := filepath.Join(cwd, po.FrontEnd.Dir, po.typescriptDefsFilename)
ldflags += " -X github.com/wailsapp/wails/lib/binding.typescriptDefinitionFilename=" + filename
}
}
return ldflags
}
func getGitConfigValue(key string) (string, error) {
output, err := exec.Command("git", "config", "--get", "--null", key).Output()
// When using --null git appends a null character (\u0000) to the command output
return strings.TrimRight(string(output), "\u0000"), err
}

343
cmd/linux.go Normal file
View file

@ -0,0 +1,343 @@
package cmd
import (
"fmt"
"net/url"
"os"
"runtime"
"strings"
"github.com/pkg/browser"
)
// LinuxDistribution is of type int
type LinuxDistribution int
const (
// Unknown is the catch-all distro
Unknown LinuxDistribution = iota
// Debian distribution
Debian
// Ubuntu distribution
Ubuntu
// Arch linux distribution
Arch
// CentOS linux distribution
CentOS
// Fedora linux distribution
Fedora
// Gentoo distribution
Gentoo
// Zorin distribution
Zorin
// Parrot distribution
Parrot
// Linuxmint distribution
Linuxmint
// VoidLinux distribution
VoidLinux
// Elementary distribution
Elementary
// Kali distribution
Kali
// Neon distribution
Neon
// ArcoLinux distribution
ArcoLinux
// Manjaro distribution
Manjaro
// ManjaroARM distribution
ManjaroARM
// Deepin distribution
Deepin
// Raspbian distribution
Raspbian
// Tumbleweed (OpenSUSE) distribution
Tumbleweed
// Leap (OpenSUSE) distribution
Leap
// ArchLabs distribution
ArchLabs
// PopOS distribution
PopOS
// Solus distribution
Solus
// Ctlos Linux distribution
Ctlos
// EndeavourOS linux distribution
EndeavourOS
// Crux linux distribution
Crux
// RHEL distribution
RHEL
// NixOS distribution
NixOS
// Artix linux distribution
ArtixLinux
//Uos distribution
Uos
)
// DistroInfo contains all the information relating to a linux distribution
type DistroInfo struct {
Distribution LinuxDistribution
Name string
ID string
Description string
Release string
}
// GetLinuxDistroInfo returns information about the running linux distribution
func GetLinuxDistroInfo() *DistroInfo {
result := &DistroInfo{
Distribution: Unknown,
ID: "unknown",
Name: "Unknown",
}
_, err := os.Stat("/etc/os-release")
if !os.IsNotExist(err) {
osRelease, _ := os.ReadFile("/etc/os-release")
result = parseOsRelease(string(osRelease))
}
return result
}
// parseOsRelease parses the given os-release data and returns
// a DistroInfo struct with the details
func parseOsRelease(osRelease string) *DistroInfo {
result := &DistroInfo{Distribution: Unknown}
// Default value
osID := "unknown"
osNAME := "Unknown"
version := ""
// Split into lines
lines := strings.Split(osRelease, "\n")
// Iterate lines
for _, line := range lines {
// Split each line by the equals char
splitLine := strings.SplitN(line, "=", 2)
// Check we have
if len(splitLine) != 2 {
continue
}
switch splitLine[0] {
case "ID":
osID = strings.ToLower(strings.Trim(splitLine[1], "\""))
case "NAME":
osNAME = strings.Trim(splitLine[1], "\"")
case "VERSION_ID":
version = strings.Trim(splitLine[1], "\"")
}
}
// Check distro name against list of distros
switch osID {
case "fedora":
result.Distribution = Fedora
case "centos":
result.Distribution = CentOS
case "rhel":
result.Distribution = RHEL
case "arch":
result.Distribution = Arch
case "archlabs":
result.Distribution = ArchLabs
case "ctlos":
result.Distribution = Ctlos
case "debian":
result.Distribution = Debian
case "ubuntu":
result.Distribution = Ubuntu
case "gentoo":
result.Distribution = Gentoo
case "zorin":
result.Distribution = Zorin
case "parrot":
result.Distribution = Parrot
case "linuxmint":
result.Distribution = Linuxmint
case "void":
result.Distribution = VoidLinux
case "elementary":
result.Distribution = Elementary
case "kali":
result.Distribution = Kali
case "neon":
result.Distribution = Neon
case "arcolinux":
result.Distribution = ArcoLinux
case "manjaro":
result.Distribution = Manjaro
case "manjaro-arm":
result.Distribution = ManjaroARM
case "deepin":
result.Distribution = Deepin
case "raspbian":
result.Distribution = Raspbian
case "opensuse-tumbleweed":
result.Distribution = Tumbleweed
case "opensuse-leap":
result.Distribution = Leap
case "pop":
result.Distribution = PopOS
case "solus":
result.Distribution = Solus
case "endeavouros":
result.Distribution = EndeavourOS
case "crux":
result.Distribution = Crux
case "nixos":
result.Distribution = NixOS
case "artix":
result.Distribution = ArtixLinux
case "uos":
result.Distribution = Uos
default:
result.Distribution = Unknown
}
result.Name = osNAME
result.ID = osID
result.Release = version
return result
}
// CheckPkgInstalled is all functions that use local programs to see if a package is installed
type CheckPkgInstalled func(string) (bool, error)
// EqueryInstalled uses equery to see if a package is installed
func EqueryInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
equery := program.FindProgram("equery")
if equery == nil {
return false, fmt.Errorf("cannont check dependencies: equery not found")
}
_, _, exitCode, _ := equery.Run("l", packageName)
return exitCode == 0, nil
}
// DpkgInstalled uses dpkg to see if a package is installed
func DpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
dpkg := program.FindProgram("dpkg")
if dpkg == nil {
return false, fmt.Errorf("cannot check dependencies: dpkg not found")
}
_, _, exitCode, _ := dpkg.Run("-L", packageName)
return exitCode == 0, nil
}
// EOpkgInstalled uses dpkg to see if a package is installed
func EOpkgInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
eopkg := program.FindProgram("eopkg")
if eopkg == nil {
return false, fmt.Errorf("cannot check dependencies: eopkg not found")
}
stdout, _, _, _ := eopkg.Run("info", packageName)
return strings.HasPrefix(stdout, "Installed"), nil
}
// PacmanInstalled uses pacman to see if a package is installed.
func PacmanInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
pacman := program.FindProgram("pacman")
if pacman == nil {
return false, fmt.Errorf("cannot check dependencies: pacman not found")
}
_, _, exitCode, _ := pacman.Run("-Qs", packageName)
return exitCode == 0, nil
}
// XbpsInstalled uses pacman to see if a package is installed.
func XbpsInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
xbpsQuery := program.FindProgram("xbps-query")
if xbpsQuery == nil {
return false, fmt.Errorf("cannot check dependencies: xbps-query not found")
}
_, _, exitCode, _ := xbpsQuery.Run("-S", packageName)
return exitCode == 0, nil
}
// RpmInstalled uses rpm to see if a package is installed
func RpmInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
rpm := program.FindProgram("rpm")
if rpm == nil {
return false, fmt.Errorf("cannot check dependencies: rpm not found")
}
_, _, exitCode, _ := rpm.Run("--query", packageName)
return exitCode == 0, nil
}
// PrtGetInstalled uses prt-get to see if a package is installed
func PrtGetInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
prtget := program.FindProgram("prt-get")
if prtget == nil {
return false, fmt.Errorf("cannot check dependencies: prt-get not found")
}
_, _, exitCode, _ := prtget.Run("isinst", packageName)
return exitCode == 0, nil
}
// NixEnvInstalled uses nix-env to see if a package is installed
func NixEnvInstalled(packageName string) (bool, error) {
program := NewProgramHelper()
nixEnv := program.FindProgram("nix-env")
if nixEnv == nil {
return false, fmt.Errorf("cannot check dependencies: nix-env not found")
}
packageName = strings.ReplaceAll(packageName, "+", `\+`)
_, _, exitCode, _ := nixEnv.Run("-q", packageName)
return exitCode == 0, nil
}
// RequestSupportForDistribution promts the user to submit a request to support their
// currently unsupported distribution
func RequestSupportForDistribution(distroInfo *DistroInfo) error {
var logger = NewLogger()
defaultError := fmt.Errorf("unable to check libraries on distribution '%s'", distroInfo.Name)
logger.Yellow("Distribution '%s' is not currently supported, but we would love to!", distroInfo.Name)
q := fmt.Sprintf("Would you like to submit a request to support distribution '%s'?", distroInfo.Name)
result := Prompt(q, "yes")
if strings.ToLower(result) != "yes" {
return defaultError
}
title := fmt.Sprintf("Support Distribution '%s'", distroInfo.Name)
var str strings.Builder
gomodule, exists := os.LookupEnv("GO111MODULE")
if !exists {
gomodule = "(Not Set)"
}
str.WriteString("\n| Name | Value |\n| ----- | ----- |\n")
str.WriteString(fmt.Sprintf("| Wails Version | %s |\n", Version))
str.WriteString(fmt.Sprintf("| Go Version | %s |\n", runtime.Version()))
str.WriteString(fmt.Sprintf("| Platform | %s |\n", runtime.GOOS))
str.WriteString(fmt.Sprintf("| Arch | %s |\n", runtime.GOARCH))
str.WriteString(fmt.Sprintf("| GO111MODULE | %s |\n", gomodule))
str.WriteString(fmt.Sprintf("| Distribution ID | %s |\n", distroInfo.ID))
str.WriteString(fmt.Sprintf("| Distribution Name | %s |\n", distroInfo.Name))
str.WriteString(fmt.Sprintf("| Distribution Version | %s |\n", distroInfo.Release))
body := fmt.Sprintf("**Description**\nDistribution '%s' is currently unsupported.\n\n**Further Information**\n\n%s\n\n*Please add any extra information here, EG: libraries that are needed to make the distribution work, or commands to install them*", distroInfo.ID, str.String())
fullURL := "https://github.com/wailsapp/wails/issues/new?"
params := "title=" + title + "&body=" + body
fmt.Println("Opening browser to file request.")
browser.OpenURL(fullURL + url.PathEscape(params))
result = Prompt("We have a guide for adding support for your distribution. Would you like to view it?", "yes")
if strings.ToLower(result) == "yes" {
browser.OpenURL("https://wails.app/guides/distro/")
}
return nil
}

46
cmd/linux_test.go Normal file
View file

@ -0,0 +1,46 @@
package cmd
import "testing"
func TestUbuntuDetection(t *testing.T) {
osrelease := `
NAME="Ubuntu"
VERSION="18.04.2 LTS (Bionic Beaver)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 18.04.2 LTS"
VERSION_ID="18.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=bionic
UBUNTU_CODENAME=bionic
`
result := parseOsRelease(osrelease)
if result.Distribution != Ubuntu {
t.Errorf("expected 'Ubuntu' ID but got '%d'", result.Distribution)
}
}
func TestTumbleweedDetection(t *testing.T) {
osrelease := `
NAME="openSUSE Tumbleweed"
# VERSION="20200414"
ID="opensuse-tumbleweed"
ID_LIKE="opensuse suse"
VERSION_ID="20200414"
PRETTY_NAME="openSUSE Tumbleweed"
ANSI_COLOR="0;32"
CPE_NAME="cpe:/o:opensuse:tumbleweed:20200414"
BUG_REPORT_URL="https://bugs.opensuse.org"
HOME_URL="https://www.opensuse.org/"
LOGO="distributor-logo"
`
result := parseOsRelease(osrelease)
if result.Distribution != Tumbleweed {
t.Errorf("expected 'Tumbleweed' ID but got '%d'", result.Distribution)
}
}

93
cmd/linuxdb.go Normal file
View file

@ -0,0 +1,93 @@
package cmd
import (
_ "embed"
"log"
"gopkg.in/yaml.v3"
)
//go:embed linuxdb.yaml
var LinuxDBYaml []byte
// LinuxDB is the database for linux distribution data.
type LinuxDB struct {
Distributions map[string]*Distribution `yaml:"distributions"`
}
// Distribution holds the os-release ID and a map of releases.
type Distribution struct {
ID string `yaml:"id"`
Releases map[string]*Release `yaml:"releases"`
}
// GetRelease attempts to return the specific Release information
// for the given release name. If there is no specific match, the
// default release data is returned.
func (d *Distribution) GetRelease(version string) *Release {
result := d.Releases[version]
if result == nil {
result = d.Releases["default"]
}
return result
}
// Release holds the name and version of the release as given by
// os-release. Programs is a slice of dependant programs required
// to be present on the local installation for Wails to function.
// Libraries is a slice of libraries that must be present for Wails
// applications to compile.
type Release struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
GccVersionCommand string `yaml:"gccversioncommand"`
Programs []*Prerequisite `yaml:"programs"`
Libraries []*Prerequisite `yaml:"libraries"`
}
// Prerequisite is a simple struct containing a program/library name
// plus the distribution specific help text indicating how to install
// it.
type Prerequisite struct {
Name string `yaml:"name"`
Help string `yaml:"help,omitempty"`
}
// Load will load the given filename from disk and attempt to
// import the data into the LinuxDB.
func (l *LinuxDB) Load(filename string) error {
if fs.FileExists(filename) {
data, err := fs.LoadAsBytes(filename)
if err != nil {
return err
}
return l.ImportData(data)
}
return nil
}
// ImportData will unmarshal the given YAML formatted data
// into the LinuxDB
func (l *LinuxDB) ImportData(data []byte) error {
return yaml.Unmarshal(data, l)
}
// GetDistro returns the Distribution information for the
// given distribution name. If the distribution is not supported,
// nil is returned.
func (l *LinuxDB) GetDistro(distro string) *Distribution {
return l.Distributions[distro]
}
// NewLinuxDB creates a new LinuxDB instance from the bundled
// linuxdb.yaml file.
func NewLinuxDB() *LinuxDB {
result := LinuxDB{
Distributions: make(map[string]*Distribution),
}
err := result.ImportData(LinuxDBYaml)
if err != nil {
log.Fatal(err)
}
return &result
}

384
cmd/linuxdb.yaml Normal file
View file

@ -0,0 +1,384 @@
---
distributions:
debian:
id: debian
releases:
default:
name: Debian
version: default
gccversioncommand: &gccdumpversion -dumpversion
programs: &debiandefaultprograms
- name: gcc
help: Please install with `sudo apt-get install build-essential` and try again
- name: pkg-config
help: Please install with `sudo apt-get install pkg-config` and try again
- name: npm
help: Please install with `curl -sL https://deb.nodesource.com/setup_12.x | sudo bash - && sudo apt-get install -y nodejs` and try again
libraries: &debiandefaultlibraries
- name: libgtk-3-dev
help: Please install with `sudo apt-get install libgtk-3-dev` and try again
- name: libwebkit2gtk-4.0-dev
help: Please install with `sudo apt-get install libwebkit2gtk-4.0-dev` and try again
ubuntu:
id: ubuntu
releases:
default:
version: default
name: Ubuntu
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
pop:
id: pop
releases:
default:
version: default
name: Pop!_OS
gccversioncommand: &gccdumpfullversion -dumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
kali:
id: kali
releases:
default:
version: default
name: Kali GNU/Linux
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
parrot:
id: parrot
releases:
default:
version: default
name: Parrot GNU/Linux
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
zorin:
id: zorin
releases:
default:
version: default
name: Zorin
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
linuxmint:
id: linuxmint
releases:
default:
version: default
name: Linux Mint
gccversioncommand: *gccdumpversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
elementary:
id: elementary
releases:
default:
version: default
name: elementary OS
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
neon:
id: neon
releases:
default:
version: default
name: KDE neon
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
deepin:
id: deepin
releases:
default:
version: default
name: Deepin
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
uos:
id: uos
releases:
default:
version: default
name: Uos
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
void:
id: void
releases:
default:
version: default
name: VoidLinux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `xbps-install base-devel` and try again
- name: pkg-config
help: Please install with `xbps-install pkg-config` and try again
- name: npm
help: Please install with `xbps-install nodejs` and try again
libraries:
- name: gtk+3-devel
help: Please install with `xbps-install gtk+3-devel` and try again
- name: webkit2gtk-devel
help: Please install with `xbps-install webkit2gtk-devel` and try again
centos:
id: centos
releases:
default:
version: default
name: CentOS Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkitgtk3-devel
help: Please install with `sudo yum install webkitgtk3-devel` and try again
rhel:
id: rhel
releases:
default:
version: default
name: Red Hat Enterprise Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install with `sudo yum install epel-release && sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkitgtk3-devel
help: Please install with `sudo yum install webkitgtk3-devel` and try again
fedora:
id: fedora
releases:
default:
version: default
name: Fedora
gccversioncommand: *gccdumpfullversion
programs:
- name: gcc
help: Please install with `sudo yum install gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo yum install pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo yum install nodejs` and try again
libraries:
- name: gtk3-devel
help: Please install with `sudo yum install gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo yum install webkit2gtk3-devel` and try again
arch:
id: arch
releases:
default:
version: default
name: Arch Linux
gccversioncommand: *gccdumpversion
programs: &archdefaultprograms
- name: gcc
help: Please install with `sudo pacman -S gcc` and try again
- name: pkgconf
help: Please install with `sudo pacman -S pkgconf` and try again
- name: npm
help: Please install with `sudo pacman -S npm` and try again
libraries: &archdefaultlibraries
- name: gtk3
help: Please install with `sudo pacman -S gtk3` and try again
- name: webkit2gtk
help: Please install with `sudo pacman -S webkit2gtk` and try again
arcolinux:
id: arcolinux
releases:
default:
version: default
name: ArcoLinux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
archlabs:
id: archlabs
releases:
default:
version: default
name: ArchLabs
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
artix:
id: artix
releases:
default:
version: default
name: Artix Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
ctlos:
id: ctlos
releases:
default:
version: default
name: Ctlos Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
endeavouros:
id: endeavouros
releases:
default:
version: default
name: EndeavourOS
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro:
id: manjaro
releases:
default:
version: default
name: Manjaro Linux
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
manjaro-arm:
id: manjaro-arm
releases:
default:
version: default
name: Manjaro-ARM
gccversioncommand: *gccdumpversion
programs: *archdefaultprograms
libraries: *archdefaultlibraries
gentoo:
id: gentoo
releases:
default:
version: default
name: Gentoo
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install using your system's package manager
- name: pkg-config
help: Please install using your system's package manager
- name: npm
help: Please install using your system's package manager
libraries:
- name: gtk+:3
help: Please install with `sudo emerge gtk+:3` and try again
- name: webkit-gtk
help: Please install with `sudo emerge webkit-gtk` and try again
raspbian:
id: raspbian
releases:
default:
version: default
name: Raspbian
gccversioncommand: *gccdumpfullversion
programs: *debiandefaultprograms
libraries: *debiandefaultlibraries
solus:
id: solus
releases:
default:
version: default
name: Solus
gccversioncommand: *gccdumpfullversion
programs: &solusdefaultprograms
- name: gcc
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: pkg-config
help: Please install with `sudo eopkg it -c system.devel` and try again
- name: npm
help: Please install with `sudo eopkg it nodejs` and try again
libraries: &solusdefaultlibraries
- name: libgtk-3-devel
help: Please install with `sudo eopkg it libgtk-3-devel` and try again
- name: libwebkit-gtk-devel
help: Please install with `sudo eopkg it libwebkit-gtk-devel` and try again
opensuse-tumbleweed:
id: opensuse-tumbleweed
releases:
default:
version: default
name: openSUSE Tumbleweed
gccversioncommand: *gccdumpfullversion
programs: &opensusedefaultprograms
- name: gcc
help: Please install with `sudo zypper in gcc-c++` and try again
- name: pkg-config
help: Please install with `sudo zypper in pkgconf-pkg-config` and try again
- name: npm
help: Please install `sudo zypper in nodejs` and try again
libraries: &opensusedefaultlibraries
- name: gtk3-devel
help: Please install with `sudo zypper in gtk3-devel` and try again
- name: webkit2gtk3-devel
help: Please install with `sudo zypper in webkit2gtk3-devel` and try again
opensuse-leap:
id: opensuse-leap
releases:
default:
version: default
name: openSUSE Leap
gccversioncommand: *gccdumpfullversion
programs: *opensusedefaultprograms
libraries: *opensusedefaultlibraries
crux:
id: crux
releases:
default:
version: default
name: Crux Linux
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `sudo prt-get depinst gcc-c++ make` and try again
- name: pkg-config
help: Please install with `sudo prt-get depinst pkg-config` and try again
- name: npm
help: Please install with `sudo prt-get depinst nodejs` and try again
libraries:
- name: gtk3
help: Please install with `sudo prt-get depinst gtk3` and try again
- name: webkitgtk
help: Please install with `sudo prt-get depinst webkitgtk` and try again
nixos:
id: nixos
releases:
default:
version: default
name: NixOS
gccversioncommand: *gccdumpversion
programs:
- name: gcc
help: Please install with `nix-env -iA nixos.gcc`
- name: pkg-config
help: Please install with `nix-env -iA nixos.pkg-config`
- name: npm
help: Please install with `nix-env -iA nixos.nodejs`
libraries:
- name: gtk+3
help: Please install with `nix-env -iA nixos.gtk3`
- name: webkitgtk
help: Please install with `nix-env -iA nixos.nodePackages.webkitgtk`

81
cmd/linuxdb_test.go Normal file
View file

@ -0,0 +1,81 @@
package cmd
import "testing"
func TestNewLinuxDB(t *testing.T) {
_ = NewLinuxDB()
}
func TestKnownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
}
func TestUnknownDistro(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("unknown")
if result != nil {
t.Error("Should get nil for distribution 'unknown'")
}
}
func TestDefaultRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Cannot get release 'default' for distro 'ubuntu'")
}
}
func TestUnknownRelease(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("ubuntu")
if result == nil {
t.Error("Cannot get distro 'ubuntu'")
}
release := result.GetRelease("16.04")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
}
func TestGetPrerequisites(t *testing.T) {
var linuxDB = NewLinuxDB()
result := linuxDB.GetDistro("debian")
if result == nil {
t.Error("Cannot get distro 'debian'")
}
release := result.GetRelease("default")
if release == nil {
t.Error("Failed to get release 'default' for unknown release version '16.04'")
}
if release.Version != "default" {
t.Errorf("Got version '%s' instead of 'default' for unknown release version '16.04'", result.ID)
}
if release.Name != "Debian" {
t.Errorf("Got Release Name '%s' instead of 'debian' for unknown release version '16.04'", release.Name)
}
if len(release.Programs) != 3 {
t.Errorf("Expected %d programs for unknown release version '16.04'", len(release.Programs))
}
if len(release.Libraries) != 2 {
t.Errorf("Expected %d libraries for unknown release version '16.04'", len(release.Libraries))
}
}

130
cmd/log.go Normal file
View file

@ -0,0 +1,130 @@
package cmd
import (
"fmt"
"strings"
"github.com/fatih/color"
)
// Logger struct
type Logger struct {
errorOnly bool
}
// NewLogger creates a new logger!
func NewLogger() *Logger {
return &Logger{errorOnly: false}
}
// SetErrorOnly ensures that only errors are logged out
func (l *Logger) SetErrorOnly(errorOnly bool) {
l.errorOnly = errorOnly
}
// Yellow - Outputs yellow text
func (l *Logger) Yellow(format string, a ...interface{}) {
if l.errorOnly {
return
}
color.New(color.FgHiYellow).PrintfFunc()(format+"\n", a...)
}
// Yellowf - Outputs yellow text without the newline
func (l *Logger) Yellowf(format string, a ...interface{}) {
if l.errorOnly {
return
}
color.New(color.FgHiYellow).PrintfFunc()(format, a...)
}
// Green - Outputs Green text
func (l *Logger) Green(format string, a ...interface{}) {
if l.errorOnly {
return
}
color.New(color.FgHiGreen).PrintfFunc()(format+"\n", a...)
}
// White - Outputs White text
func (l *Logger) White(format string, a ...interface{}) {
if l.errorOnly {
return
}
color.New(color.FgHiWhite).PrintfFunc()(format+"\n", a...)
}
// WhiteUnderline - Outputs White text with underline
func (l *Logger) WhiteUnderline(format string, a ...interface{}) {
if l.errorOnly {
return
}
l.White(format, a...)
l.White(l.underline(format))
}
// YellowUnderline - Outputs Yellow text with underline
func (l *Logger) YellowUnderline(format string, a ...interface{}) {
if l.errorOnly {
return
}
l.Yellow(format, a...)
l.Yellow(l.underline(format))
}
// underline returns a string of a line, the length of the message given to it
func (l *Logger) underline(message string) string {
if l.errorOnly {
return ""
}
return strings.Repeat("-", len(message))
}
// Red - Outputs Red text
func (l *Logger) Red(format string, a ...interface{}) {
if l.errorOnly {
return
}
color.New(color.FgHiRed).PrintfFunc()(format+"\n", a...)
}
// Error - Outputs an Error message
func (l *Logger) Error(format string, a ...interface{}) {
color.New(color.FgHiRed).PrintfFunc()("Error: "+format+"\n", a...)
}
// PrintSmallBanner prints a condensed banner
func (l *Logger) PrintSmallBanner(message ...string) {
yellow := color.New(color.FgYellow).SprintFunc()
red := color.New(color.FgRed).SprintFunc()
msg := ""
if len(message) > 0 {
msg = " - " + message[0]
}
fmt.Printf("%s %s%s\n", yellow("Wails"), red(Version), msg)
}
// PrintBanner prints the Wails banner before running commands
func (l *Logger) PrintBanner() error {
banner1 := ` _ __ _ __
| | / /___ _(_) /____
| | /| / / __ ` + "`" + `/ / / ___/
| |/ |/ / /_/ / / (__ ) `
banner2 := `|__/|__/\__,_/_/_/____/ `
l.Yellowf(banner1)
l.Red(Version)
l.Yellowf(banner2)
l.Green("https://wails.app")
l.White("The lightweight framework for web-like apps")
fmt.Println()
return nil
}

427
cmd/package.go Normal file
View file

@ -0,0 +1,427 @@
package cmd
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"image"
"image/png"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"text/template"
"time"
"github.com/jackmordaunt/icns"
"golang.org/x/image/draw"
)
// PackageHelper helps with the 'wails package' command
type PackageHelper struct {
platform string
fs *FSHelper
log *Logger
system *SystemHelper
}
// NewPackageHelper creates a new PackageHelper!
func NewPackageHelper(platform string) *PackageHelper {
return &PackageHelper{
platform: platform,
fs: NewFSHelper(),
log: NewLogger(),
system: NewSystemHelper(),
}
}
type plistData struct {
Title string
Exe string
PackageID string
Version string
Author string
Date string
}
func newPlistData(title, exe, packageID, version, author string) *plistData {
now := time.Now().Format(time.RFC822)
return &plistData{
Title: title,
Exe: exe,
Version: version,
PackageID: packageID,
Author: author,
Date: now,
}
}
type windowsIcoHeader struct {
_ uint16
imageType uint16
imageCount uint16
}
type windowsIcoDescriptor struct {
width uint8
height uint8
colours uint8
_ uint8
planes uint16
bpp uint16
size uint32
offset uint32
}
type windowsIcoContainer struct {
Header windowsIcoDescriptor
Data []byte
}
func generateWindowsIcon(pngFilename string, iconfile string) error {
sizes := []int{256, 128, 64, 48, 32, 16}
pngfile, err := os.Open(pngFilename)
if err != nil {
return err
}
defer pngfile.Close()
pngdata, err := png.Decode(pngfile)
if err != nil {
return err
}
icons := []windowsIcoContainer{}
for _, size := range sizes {
rect := image.Rect(0, 0, int(size), int(size))
rawdata := image.NewRGBA(rect)
scale := draw.CatmullRom
scale.Scale(rawdata, rect, pngdata, pngdata.Bounds(), draw.Over, nil)
icondata := new(bytes.Buffer)
writer := bufio.NewWriter(icondata)
err = png.Encode(writer, rawdata)
if err != nil {
return err
}
writer.Flush()
imgSize := size
if imgSize >= 256 {
imgSize = 0
}
data := icondata.Bytes()
icn := windowsIcoContainer{
Header: windowsIcoDescriptor{
width: uint8(imgSize),
height: uint8(imgSize),
planes: 1,
bpp: 32,
size: uint32(len(data)),
},
Data: data,
}
icons = append(icons, icn)
}
outfile, err := os.Create(iconfile)
if err != nil {
return err
}
defer outfile.Close()
ico := windowsIcoHeader{
imageType: 1,
imageCount: uint16(len(sizes)),
}
err = binary.Write(outfile, binary.LittleEndian, ico)
if err != nil {
return err
}
offset := uint32(6 + 16*len(sizes))
for _, icon := range icons {
icon.Header.offset = offset
err = binary.Write(outfile, binary.LittleEndian, icon.Header)
if err != nil {
return err
}
offset += icon.Header.size
}
for _, icon := range icons {
_, err = outfile.Write(icon.Data)
if err != nil {
return err
}
}
return nil
}
func defaultString(val string, defaultVal string) string {
if val != "" {
return val
}
return defaultVal
}
func (b *PackageHelper) getPackageFileBaseDir() string {
// Calculate template base dir
_, filename, _, _ := runtime.Caller(1)
return filepath.Join(path.Dir(filename), "packages", b.platform)
}
// Package the application into a platform specific package
func (b *PackageHelper) Package(po *ProjectOptions) error {
switch b.platform {
case "darwin":
return b.packageOSX(po)
case "windows":
return b.PackageWindows(po, true)
case "linux":
return b.packageLinux(po)
default:
return fmt.Errorf("platform '%s' not supported for bundling yet", b.platform)
}
}
func (b *PackageHelper) packageLinux(po *ProjectOptions) error {
return nil
}
// Package the application for OSX
func (b *PackageHelper) packageOSX(po *ProjectOptions) error {
build := path.Join(b.fs.Cwd(), "build")
system := NewSystemHelper()
config, err := system.LoadConfig()
if err != nil {
return err
}
name := defaultString(po.Name, "WailsTest")
exe := defaultString(po.BinaryName, name)
version := defaultString(po.Version, "0.1.0")
author := defaultString(config.Name, "Anonymous")
packageID := strings.Join([]string{"wails", name, version}, ".")
plistData := newPlistData(name, exe, packageID, version, author)
appname := po.Name + ".app"
plistFilename := path.Join(build, appname, "Contents", "Info.plist")
customPlist := path.Join(b.fs.Cwd(), "info.plist")
// Check binary exists
source := path.Join(build, exe)
if po.CrossCompile == true {
file, err := b.fs.FindFile(build, "darwin")
if err != nil {
return err
}
source = path.Join(build, file)
}
if !b.fs.FileExists(source) {
// We need to build!
return fmt.Errorf("Target '%s' not available. Has it been compiled yet?", source)
}
// Remove the existing package
os.RemoveAll(appname)
// Create directories
exeDir := path.Join(build, appname, "/Contents/MacOS")
b.fs.MkDirs(exeDir, 0755)
resourceDir := path.Join(build, appname, "/Contents/Resources")
b.fs.MkDirs(resourceDir, 0755)
// Do we have a custom plist in the project directory?
if !fs.FileExists(customPlist) {
// No - create a new plist from our defaults
tmpl := template.New("infoPlist")
plistFile := filepath.Join(b.getPackageFileBaseDir(), "info.plist")
infoPlist, err := os.ReadFile(plistFile)
if err != nil {
return err
}
tmpl.Parse(string(infoPlist))
// Write the template to a buffer
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, plistData)
if err != nil {
return err
}
// Save to the package
err = os.WriteFile(plistFilename, tpl.Bytes(), 0644)
if err != nil {
return err
}
// Also write to project directory for customisation
err = os.WriteFile(customPlist, tpl.Bytes(), 0644)
if err != nil {
return err
}
} else {
// Yes - we have a plist. Copy it to the package verbatim
err = fs.CopyFile(customPlist, plistFilename)
if err != nil {
return err
}
}
// Copy executable
target := path.Join(exeDir, exe)
err = b.fs.CopyFile(source, target)
if err != nil {
return err
}
err = os.Chmod(target, 0755)
if err != nil {
return err
}
err = b.packageIconOSX(resourceDir)
return err
}
// CleanWindows removes any windows related files found in the directory
func (b *PackageHelper) CleanWindows(po *ProjectOptions) {
pdir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
exts := []string{".ico", ".exe.manifest", ".rc", "-res.syso"}
rsrcs := []string{}
for _, ext := range exts {
rsrcs = append(rsrcs, filepath.Join(pdir, basename+ext))
}
b.fs.RemoveFiles(rsrcs, true)
}
// PackageWindows packages the application for windows platforms
func (b *PackageHelper) PackageWindows(po *ProjectOptions, cleanUp bool) error {
outputDir := b.fs.Cwd()
basename := strings.TrimSuffix(po.BinaryName, ".exe")
// Copy default icon if needed
icon, err := b.copyIcon()
if err != nil {
return err
}
// Generate icon from PNG if it doesn't exist
if !fs.FileExists(basename + ".ico") {
err = generateWindowsIcon(icon, basename+".ico")
if err != nil {
return err
}
}
// Copy manifest
tgtManifestFile := filepath.Join(outputDir, basename+".exe.manifest")
if !b.fs.FileExists(tgtManifestFile) {
srcManifestfile := filepath.Join(b.getPackageFileBaseDir(), "wails.exe.manifest")
err := b.fs.CopyFile(srcManifestfile, tgtManifestFile)
if err != nil {
return err
}
}
// Copy rc file
tgtRCFile := filepath.Join(outputDir, basename+".rc")
if !b.fs.FileExists(tgtRCFile) {
srcRCfile := filepath.Join(b.getPackageFileBaseDir(), "wails.rc")
rcfilebytes, err := os.ReadFile(srcRCfile)
if err != nil {
return err
}
rcfiledata := strings.Replace(string(rcfilebytes), "$NAME$", basename, -1)
err = os.WriteFile(tgtRCFile, []byte(rcfiledata), 0755)
if err != nil {
return err
}
}
// Build syso
sysofile := filepath.Join(outputDir, basename+"-res.syso")
// cross-compile
if b.platform != runtime.GOOS {
args := []string{
"docker", "run", "--rm",
"-v", outputDir + ":/build",
"--entrypoint", "/bin/sh",
"wailsapp/xgo:1.16.3",
"-c", "/usr/bin/x86_64-w64-mingw32-windres -o /build/" + basename + "-res.syso /build/" + basename + ".rc",
}
if err := NewProgramHelper().RunCommandArray(args); err != nil {
return err
}
} else {
batfile, err := fs.LocalDir(".")
if err != nil {
return err
}
windresBatFile := filepath.Join(batfile.fullPath, "windres.bat")
windresCommand := []string{windresBatFile, sysofile, tgtRCFile}
err = NewProgramHelper().RunCommandArray(windresCommand)
if err != nil {
return err
}
}
return nil
}
func (b *PackageHelper) copyIcon() (string, error) {
// TODO: Read this from project.json
const appIconFilename = "appicon.png"
srcIcon := path.Join(b.fs.Cwd(), appIconFilename)
// Check if appicon.png exists
if !b.fs.FileExists(srcIcon) {
// Install default icon
iconfile := filepath.Join(b.getPackageFileBaseDir(), "icon.png")
iconData, err := os.ReadFile(iconfile)
if err != nil {
return "", err
}
err = os.WriteFile(srcIcon, iconData, 0644)
if err != nil {
return "", err
}
}
return srcIcon, nil
}
func (b *PackageHelper) packageIconOSX(resourceDir string) error {
srcIcon, err := b.copyIcon()
if err != nil {
return err
}
tgtBundle := path.Join(resourceDir, "iconfile.icns")
imageFile, err := os.Open(srcIcon)
if err != nil {
return err
}
defer imageFile.Close()
srcImg, _, err := image.Decode(imageFile)
if err != nil {
return err
}
dest, err := os.Create(tgtBundle)
if err != nil {
return err
}
defer dest.Close()
return icns.Encode(dest, srcImg)
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View file

@ -0,0 +1,12 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>CFBundlePackageType</key><string>APPL</string>
<key>CFBundleName</key><string>{{.Title}}</string>
<key>CFBundleExecutable</key><string>{{.Exe}}</string>
<key>CFBundleIdentifier</key><string>{{.PackageID}}</string>
<key>CFBundleVersion</key><string>{{.Version}}</string>
<key>CFBundleGetInfoString</key><string>Built by {{.Author}} at {{.Date}} using Wails (https://wails.app)</string>
<key>CFBundleShortVersionString</key><string>{{.Version}}</string>
<key>CFBundleIconFile</key><string>iconfile</string>
<key>NSHighResolutionCapable</key><string>true</string>
</dict></plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="amd64"/>
<asmv3:application>
<asmv3:windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware> <!-- fallback for Windows 7 and 8 -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">permonitorv2,permonitor</dpiAwareness> <!-- falls back to per-monitor if per-monitor v2 is not supported -->
<gdiScaling xmlns="http://schemas.microsoft.com/SMI/2017/WindowsSettings">true</gdiScaling> <!-- enables GDI DPI scaling -->
</asmv3:windowsSettings>
</asmv3:application>
</assembly>

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

View file

@ -0,0 +1,2 @@
100 ICON "$NAME$.ico"
110 24 "$NAME$.exe.manifest"

100
cmd/prerequisites.go Normal file
View file

@ -0,0 +1,100 @@
package cmd
import (
"fmt"
"runtime"
)
func newPrerequisite(name, help string) *Prerequisite {
return &Prerequisite{Name: name, Help: help}
}
// Prerequisites is a list of things required to use Wails
type Prerequisites []*Prerequisite
// Add given prereq object to list
func (p *Prerequisites) Add(prereq *Prerequisite) {
*p = append(*p, prereq)
}
// GetRequiredPrograms returns a list of programs required for the platform
func GetRequiredPrograms() (*Prerequisites, error) {
switch runtime.GOOS {
case "darwin":
return getRequiredProgramsOSX(), nil
case "linux":
return getRequiredProgramsLinux(), nil
case "windows":
return getRequiredProgramsWindows(), nil
default:
return nil, fmt.Errorf("platform '%s' not supported at this time", runtime.GOOS)
}
}
func getRequiredProgramsOSX() *Prerequisites {
result := &Prerequisites{}
result.Add(newPrerequisite("clang", "Please install with `xcode-select --install` and try again"))
result.Add(newPrerequisite("npm", "Please install from https://nodejs.org/en/download/ and try again"))
return result
}
func getRequiredProgramsLinux() *Prerequisites {
result := &Prerequisites{}
distroInfo := GetLinuxDistroInfo()
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, program := range release.Programs {
result.Add(program)
}
}
return result
}
// TODO: Test this on Windows
func getRequiredProgramsWindows() *Prerequisites {
result := &Prerequisites{}
result.Add(newPrerequisite("gcc", "Please install gcc from here and try again: http://tdm-gcc.tdragon.net/download. You will need to add the bin directory to your path, EG: C:\\TDM-GCC-64\\bin\\"))
result.Add(newPrerequisite("npm", "Please install node/npm from here and try again: https://nodejs.org/en/download/"))
return result
}
// GetRequiredLibraries returns a list of libraries (packages) required for the platform
func GetRequiredLibraries() (*Prerequisites, error) {
switch runtime.GOOS {
case "darwin":
return getRequiredLibrariesOSX()
case "linux":
return getRequiredLibrariesLinux()
case "windows":
return getRequiredLibrariesWindows()
default:
return nil, fmt.Errorf("platform '%s' not supported at this time", runtime.GOOS)
}
}
func getRequiredLibrariesOSX() (*Prerequisites, error) {
result := &Prerequisites{}
return result, nil
}
func getRequiredLibrariesLinux() (*Prerequisites, error) {
result := &Prerequisites{}
// The Linux Distribution DB
distroInfo := GetLinuxDistroInfo()
if distroInfo.Distribution != Unknown {
var linuxDB = NewLinuxDB()
distro := linuxDB.GetDistro(distroInfo.ID)
release := distro.GetRelease(distroInfo.Release)
for _, library := range release.Libraries {
result.Add(library)
}
}
return result, nil
}
func getRequiredLibrariesWindows() (*Prerequisites, error) {
result := &Prerequisites{}
return result, nil
}

162
cmd/program.go Normal file
View file

@ -0,0 +1,162 @@
package cmd
import (
"bytes"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
)
// ProgramHelper - Utility functions around installed applications
type ProgramHelper struct {
shell *ShellHelper
verbose bool
}
// NewProgramHelper - Creates a new ProgramHelper
func NewProgramHelper(verbose ...bool) *ProgramHelper {
result := &ProgramHelper{
shell: NewShellHelper(),
}
if len(verbose) > 0 {
result.verbose = verbose[0]
if result.verbose {
result.shell.SetVerbose()
}
}
return result
}
// IsInstalled tries to determine if the given binary name is installed
func (p *ProgramHelper) IsInstalled(programName string) bool {
_, err := exec.LookPath(programName)
return err == nil
}
// Program - A struct to define an installed application/binary
type Program struct {
Name string `json:"name"`
Path string `json:"path"`
verbose bool
}
// FindProgram attempts to find the given program on the system.FindProgram
// Returns a struct with the name and path to the program
func (p *ProgramHelper) FindProgram(programName string) *Program {
path, err := exec.LookPath(programName)
if err != nil {
return nil
}
path, err = filepath.Abs(path)
if err != nil {
return nil
}
return &Program{
Name: programName,
Path: path,
verbose: p.verbose,
}
}
// GetFullPathToBinary returns the full path the the current binary
func (p *Program) GetFullPathToBinary() (string, error) {
return filepath.Abs(p.Path)
}
// Run will execute the program with the given parameters
// Returns stdout + stderr as strings and an error if one occurred
func (p *Program) Run(vars ...string) (stdout, stderr string, exitCode int, err error) {
command, err := p.GetFullPathToBinary()
if err != nil {
return "", "", 1, err
}
cmd := exec.Command(command, vars...)
if !p.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
// https://stackoverflow.com/questions/10385551/get-exit-code-go
if err != nil {
// try to get the exit code
if exitError, ok := err.(*exec.ExitError); ok {
ws := exitError.Sys().(syscall.WaitStatus)
exitCode = ws.ExitStatus()
} else {
exitCode = 1
if stderr == "" {
stderr = err.Error()
}
}
} else {
// success, exitCode should be 0 if go is ok
ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
exitCode = ws.ExitStatus()
}
return
}
// InstallGoPackage installs the given Go package
func (p *ProgramHelper) InstallGoPackage(packageName string) error {
args := strings.Split("get "+packageName, " ")
_, stderr, err := p.shell.Run("go", args...)
if err != nil {
fmt.Println(stderr)
}
return err
}
// InstallNPMPackage installs the given npm package
func (p *ProgramHelper) InstallNPMPackage(packageName string, save bool) error {
args := strings.Split("install "+packageName, " ")
if save {
args = append(args, "--save")
}
_, stderr, err := p.shell.Run("npm", args...)
if err != nil {
fmt.Println(stderr)
}
return err
}
// RunCommand runs the given command
func (p *ProgramHelper) RunCommand(command string) error {
args := strings.Split(command, " ")
return p.RunCommandArray(args)
}
// RunCommandArray runs the command specified in the array
func (p *ProgramHelper) RunCommandArray(args []string, dir ...string) error {
programCommand := args[0]
// TODO: Run FindProgram here and get the full path to the exe
program, err := exec.LookPath(programCommand)
if err != nil {
fmt.Printf("ERROR: Looks like '%s' isn't installed. Please install and try again.", programCommand)
return err
}
args = args[1:]
var stderr string
var stdout string
if len(dir) > 0 {
stdout, stderr, err = p.shell.RunInDirectory(dir[0], program, args...)
} else {
stdout, stderr, err = p.shell.Run(program, args...)
}
if err != nil {
fmt.Println(stderr)
fmt.Println(stdout)
}
return err
}

406
cmd/project.go Normal file
View file

@ -0,0 +1,406 @@
package cmd
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"github.com/leaanthony/slicer"
)
// PackageManager indicates different package managers
type PackageManager int
const (
// UNKNOWN package manager
UNKNOWN PackageManager = iota
// NPM package manager
NPM
// YARN package manager
YARN
)
type author struct {
Name string `json:"name"`
Email string `json:"email"`
}
type frontend struct {
Dir string `json:"dir"`
Install string `json:"install"`
Build string `json:"build"`
Bridge string `json:"bridge"`
Serve string `json:"serve"`
}
type framework struct {
Name string `json:"name"`
BuildTag string `json:"buildtag"`
Options map[string]string `json:"options,omitempty"`
}
// ProjectHelper is a helper struct for managing projects
type ProjectHelper struct {
log *Logger
system *SystemHelper
templates *TemplateHelper
}
// NewProjectHelper creates a new Project helper struct
func NewProjectHelper() *ProjectHelper {
return &ProjectHelper{
log: NewLogger(),
system: NewSystemHelper(),
templates: NewTemplateHelper(),
}
}
// GenerateProject generates a new project using the options given
func (ph *ProjectHelper) GenerateProject(projectOptions *ProjectOptions) error {
// Calculate project path
projectPath, err := filepath.Abs(projectOptions.OutputDirectory)
if err != nil {
return err
}
_ = projectPath
if fs.DirExists(projectPath) {
return fmt.Errorf("directory '%s' already exists", projectPath)
}
// Create project directory
err = fs.MkDir(projectPath)
if err != nil {
return err
}
// Create and save project config
err = projectOptions.WriteProjectConfig()
if err != nil {
return err
}
err = ph.templates.InstallTemplate(projectPath, projectOptions)
if err != nil {
return err
}
// // If we are on windows, dump a windows_resource.json
// if runtime.GOOS == "windows" {
// ph.GenerateWindowsResourceConfig(projectOptions)
// }
return nil
}
// // GenerateWindowsResourceConfig generates the default windows resource file
// func (ph *ProjectHelper) GenerateWindowsResourceConfig(po *ProjectOptions) {
// fmt.Println(buffer.String())
// // vi.Build()
// // vi.Walk()
// // err := vi.WriteSyso(outPath, runtime.GOARCH)
// }
// LoadProjectConfig loads the project config from the given directory
func (ph *ProjectHelper) LoadProjectConfig(dir string) (*ProjectOptions, error) {
po := ph.NewProjectOptions()
err := po.LoadConfig(dir)
return po, err
}
// NewProjectOptions creates a new default set of project options
func (ph *ProjectHelper) NewProjectOptions() *ProjectOptions {
result := ProjectOptions{
Name: "",
Description: "Enter your project description",
Version: "0.1.0",
BinaryName: "",
system: ph.system,
log: ph.log,
templates: ph.templates,
Author: &author{},
}
// Populate system config
config, err := ph.system.LoadConfig()
if err == nil {
result.Author.Name = config.Name
result.Author.Email = config.Email
}
return &result
}
// ProjectOptions holds all the options available for a project
type ProjectOptions struct {
Name string `json:"name"`
Description string `json:"description"`
Author *author `json:"author,omitempty"`
Version string `json:"version"`
OutputDirectory string `json:"-"`
UseDefaults bool `json:"-"`
Template string `json:"-"`
BinaryName string `json:"binaryname"`
FrontEnd *frontend `json:"frontend,omitempty"`
Tags string `json:"tags"`
NPMProjectName string `json:"-"`
system *SystemHelper
log *Logger
templates *TemplateHelper
selectedTemplate *TemplateDetails
WailsVersion string
typescriptDefsFilename string
Verbose bool `json:"-"`
CrossCompile bool
Platform string
Architecture string
LdFlags string
GoPath string
UseFirebug bool
// Supported platforms
Platforms []string `json:"platforms,omitempty"`
}
// PlatformSupported returns true if the template is supported
// on the current platform
func (po *ProjectOptions) PlatformSupported() bool {
// Default is all platforms supported
if len(po.Platforms) == 0 {
return true
}
// Check that the platform is in the list
platformsSupported := slicer.String(po.Platforms)
return platformsSupported.Contains(runtime.GOOS)
}
// Defaults sets the default project template
func (po *ProjectOptions) Defaults() {
po.Template = "vuebasic"
po.WailsVersion = Version
}
// SetTypescriptDefsFilename indicates that we want to generate typescript bindings to the given file
func (po *ProjectOptions) SetTypescriptDefsFilename(filename string) {
po.typescriptDefsFilename = filename
}
// GetNPMBinaryName returns the type of package manager used by the project
func (po *ProjectOptions) GetNPMBinaryName() (PackageManager, error) {
if po.FrontEnd == nil {
return UNKNOWN, fmt.Errorf("No frontend specified in project options")
}
if strings.Index(po.FrontEnd.Install, "npm") > -1 {
return NPM, nil
}
if strings.Index(po.FrontEnd.Install, "yarn") > -1 {
return YARN, nil
}
return UNKNOWN, nil
}
// PromptForInputs asks the user to input project details
func (po *ProjectOptions) PromptForInputs() error {
processProjectName(po)
processBinaryName(po)
err := processOutputDirectory(po)
if err != nil {
return err
}
// Process Templates
templateList := slicer.Interface()
options := slicer.String()
templateDetails, err := po.templates.GetTemplateDetails()
if err != nil {
return err
}
if po.Template != "" {
// Check template is valid if given
if templateDetails[po.Template] == nil {
keys := make([]string, 0, len(templateDetails))
for k := range templateDetails {
keys = append(keys, k)
}
return fmt.Errorf("invalid template name '%s'. Valid options: %s", po.Template, strings.Join(keys, ", "))
}
po.selectedTemplate = templateDetails[po.Template]
} else {
keys := make([]string, 0)
for k := range templateDetails {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
templateDetail := templateDetails[k]
templateList.Add(templateDetail)
if !templateDetail.Metadata.PlatformSupported() {
templateDetail.Metadata.Name = "* " + templateDetail.Metadata.Name
}
options.Add(fmt.Sprintf("%s - %s", templateDetail.Metadata.Name, templateDetail.Metadata.ShortDescription))
}
templateIndex := 0
if len(options.AsSlice()) > 1 {
templateIndex = PromptSelection("Please select a template (* means unsupported on current platform)", options.AsSlice(), 0)
}
if len(templateList.AsSlice()) == 0 {
return fmt.Errorf("aborting: no templates found")
}
// After selection do this....
po.selectedTemplate = templateList.AsSlice()[templateIndex].(*TemplateDetails)
}
po.selectedTemplate.Metadata.Name = strings.TrimPrefix(po.selectedTemplate.Metadata.Name, "* ")
if !po.selectedTemplate.Metadata.PlatformSupported() {
println("WARNING: This template is unsupported on this platform!")
}
fmt.Println("Template: " + po.selectedTemplate.Metadata.Name)
// Setup NPM Project name
po.NPMProjectName = strings.ToLower(strings.Replace(po.Name, " ", "_", -1))
// Fix template name
po.Template = strings.Split(po.selectedTemplate.Path, string(os.PathSeparator))[0]
// // Populate template details
templateMetadata := po.selectedTemplate.Metadata
err = processTemplateMetadata(templateMetadata, po)
if err != nil {
return err
}
return nil
}
// WriteProjectConfig writes the project configuration into
// the project directory
func (po *ProjectOptions) WriteProjectConfig() error {
targetDir, err := filepath.Abs(po.OutputDirectory)
if err != nil {
return err
}
targetFile := filepath.Join(targetDir, "project.json")
filedata, err := json.MarshalIndent(po, "", " ")
if err != nil {
return err
}
return os.WriteFile(targetFile, filedata, 0600)
}
// LoadConfig loads the project configuration file from the
// given directory
func (po *ProjectOptions) LoadConfig(projectDir string) error {
targetFile := filepath.Join(projectDir, "project.json")
rawBytes, err := os.ReadFile(targetFile)
if err != nil {
return err
}
return json.Unmarshal(rawBytes, po)
}
func computeBinaryName(projectName string) string {
if projectName == "" {
return ""
}
var binaryNameComputed = strings.ToLower(projectName)
binaryNameComputed = strings.Replace(binaryNameComputed, " ", "-", -1)
binaryNameComputed = strings.Replace(binaryNameComputed, string(filepath.Separator), "-", -1)
binaryNameComputed = strings.Replace(binaryNameComputed, ":", "-", -1)
return binaryNameComputed
}
func processOutputDirectory(po *ProjectOptions) error {
// po.OutputDirectory
if po.OutputDirectory == "" {
po.OutputDirectory = PromptRequired("Project directory name", computeBinaryName(po.Name))
}
projectPath, err := filepath.Abs(po.OutputDirectory)
if err != nil {
return err
}
if NewFSHelper().DirExists(projectPath) {
return fmt.Errorf("directory '%s' already exists", projectPath)
}
fmt.Println("Project Directory: " + po.OutputDirectory)
return nil
}
func processProjectName(po *ProjectOptions) {
if po.Name == "" {
po.Name = Prompt("The name of the project", "My Project")
}
fmt.Println("Project Name: " + po.Name)
}
func processBinaryName(po *ProjectOptions) {
if po.BinaryName == "" {
var binaryNameComputed = computeBinaryName(po.Name)
po.BinaryName = Prompt("The output binary name", binaryNameComputed)
}
fmt.Println("Output binary Name: " + po.BinaryName)
}
func processTemplateMetadata(templateMetadata *TemplateMetadata, po *ProjectOptions) error {
if templateMetadata.FrontendDir != "" {
po.FrontEnd = &frontend{}
po.FrontEnd.Dir = templateMetadata.FrontendDir
}
if templateMetadata.Install != "" {
if po.FrontEnd == nil {
return fmt.Errorf("install set in template metadata but not frontenddir")
}
po.FrontEnd.Install = templateMetadata.Install
}
if templateMetadata.Build != "" {
if po.FrontEnd == nil {
return fmt.Errorf("build set in template metadata but not frontenddir")
}
po.FrontEnd.Build = templateMetadata.Build
}
if templateMetadata.Bridge != "" {
if po.FrontEnd == nil {
return fmt.Errorf("bridge set in template metadata but not frontenddir")
}
po.FrontEnd.Bridge = templateMetadata.Bridge
}
if templateMetadata.Serve != "" {
if po.FrontEnd == nil {
return fmt.Errorf("serve set in template metadata but not frontenddir")
}
po.FrontEnd.Serve = templateMetadata.Serve
}
// Save platforms
po.Platforms = templateMetadata.Platforms
return nil
}

80
cmd/prompt.go Normal file
View file

@ -0,0 +1,80 @@
package cmd
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Prompt asks the user for a value
func Prompt(question string, defaultValue ...string) string {
var answer string
if len(defaultValue) > 0 {
answer = defaultValue[0]
question = fmt.Sprintf("%s (%s)", question, answer)
}
fmt.Printf(question + ": ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
if input != "" {
answer = input
}
return answer
}
// PromptRequired calls Prompt repeatedly until a value is given
func PromptRequired(question string, defaultValue ...string) string {
for {
result := Prompt(question, defaultValue...)
if result != "" {
return result
}
}
}
// PromptSelection asks the user to choose an option
func PromptSelection(question string, options []string, optionalDefaultValue ...int) int {
defaultValue := -1
message := "Please choose an option"
fmt.Println(question + ":")
if len(optionalDefaultValue) > 0 {
defaultValue = optionalDefaultValue[0] + 1
message = fmt.Sprintf("%s [%d]", message, defaultValue)
}
for index, option := range options {
fmt.Printf(" %d: %s\n", index+1, option)
}
selectedValue := -1
for {
choice := Prompt(message)
if choice == "" && defaultValue > -1 {
selectedValue = defaultValue - 1
break
}
// index
number, err := strconv.Atoi(choice)
if err == nil {
if number > 0 && number <= len(options) {
selectedValue = number - 1
break
} else {
continue
}
}
}
return selectedValue
}

106
cmd/semver.go Normal file
View file

@ -0,0 +1,106 @@
package cmd
import (
"fmt"
"github.com/Masterminds/semver"
)
// SemanticVersion is a struct containing a semantic version
type SemanticVersion struct {
Version *semver.Version
}
// NewSemanticVersion creates a new SemanticVersion object with the given version string
func NewSemanticVersion(version string) (*SemanticVersion, error) {
semverVersion, err := semver.NewVersion(version)
if err != nil {
return nil, err
}
return &SemanticVersion{
Version: semverVersion,
}, nil
}
// IsRelease returns true if it's a release version
func (s *SemanticVersion) IsRelease() bool {
// Limit to v1
if s.Version.Major() != 1 {
return false
}
return len(s.Version.Prerelease()) == 0 && len(s.Version.Metadata()) == 0
}
// IsPreRelease returns true if it's a prerelease version
func (s *SemanticVersion) IsPreRelease() bool {
// Limit to v1
if s.Version.Major() != 1 {
return false
}
return len(s.Version.Prerelease()) > 0
}
func (s *SemanticVersion) String() string {
return s.Version.String()
}
// IsGreaterThan returns true if this version is greater than the given version
func (s *SemanticVersion) IsGreaterThan(version *SemanticVersion) (bool, error) {
// Set up new constraint
constraint, err := semver.NewConstraint("> " + version.Version.String())
if err != nil {
return false, err
}
// Check if the desired one is greater than the requested on
success, msgs := constraint.Validate(s.Version)
if !success {
return false, msgs[0]
}
return true, nil
}
// IsGreaterThanOrEqual returns true if this version is greater than or equal the given version
func (s *SemanticVersion) IsGreaterThanOrEqual(version *SemanticVersion) (bool, error) {
// Set up new constraint
constraint, err := semver.NewConstraint(">= " + version.Version.String())
if err != nil {
return false, err
}
// Check if the desired one is greater than the requested on
success, msgs := constraint.Validate(s.Version)
if !success {
return false, msgs[0]
}
return true, nil
}
// MainVersion returns the main version of any version+prerelease+metadata
// EG: MainVersion("1.2.3-pre") => "1.2.3"
func (s *SemanticVersion) MainVersion() *SemanticVersion {
mainVersion := fmt.Sprintf("%d.%d.%d", s.Version.Major(), s.Version.Minor(), s.Version.Patch())
result, _ := NewSemanticVersion(mainVersion)
return result
}
// SemverCollection is a collection of SemanticVersion objects
type SemverCollection []*SemanticVersion
// Len returns the length of a collection. The number of Version instances
// on the slice.
func (c SemverCollection) Len() int {
return len(c)
}
// Less is needed for the sort interface to compare two Version objects on the
// slice. If checks if one is less than the other.
func (c SemverCollection) Less(i, j int) bool {
return c[i].Version.LessThan(c[j].Version)
}
// Swap is needed for the sort interface to replace the Version objects
// at two different positions in the slice.
func (c SemverCollection) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}

65
cmd/semver_test.go Normal file
View file

@ -0,0 +1,65 @@
package cmd
import (
"testing"
)
func TestSemanticVersion_IsPreRelease(t *testing.T) {
tests := []struct {
name string
version string
want bool
}{
{"v1.6.7-pre0", "v1.6.7-pre0", true},
{"v2.6.7+pre0", "v2.6.7+pre0", false},
{"v2.6.7", "v2.6.7", false},
{"v2.0.0+alpha.1", "v2.0.0+alpha.1", false},
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
{"v1.6.7", "v1.6.7", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
semanticversion, err := NewSemanticVersion(tt.version)
if err != nil {
t.Errorf("Invalid semantic version: %s", semanticversion)
return
}
s := &SemanticVersion{
Version: semanticversion.Version,
}
if got := s.IsPreRelease(); got != tt.want {
t.Errorf("IsPreRelease() = %v, want %v", got, tt.want)
}
})
}
}
func TestSemanticVersion_IsRelease(t *testing.T) {
tests := []struct {
name string
version string
want bool
}{
{"v1.6.7", "v1.6.7", true},
{"v2.6.7-pre0", "v2.6.7-pre0", false},
{"v2.6.7", "v2.6.7", false},
{"v2.6.7+release", "v2.6.7+release", false},
{"v2.0.0-alpha.1", "v2.0.0-alpha.1", false},
{"v1.6.7-pre0", "v1.6.7-pre0", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
semanticversion, err := NewSemanticVersion(tt.version)
if err != nil {
t.Errorf("Invalid semantic version: %s", semanticversion)
return
}
s := &SemanticVersion{
Version: semanticversion.Version,
}
if got := s.IsRelease(); got != tt.want {
t.Errorf("IsRelease() = %v, want %v", got, tt.want)
}
})
}
}

61
cmd/shell.go Normal file
View file

@ -0,0 +1,61 @@
package cmd
import (
"bytes"
"os"
"os/exec"
)
// ShellHelper helps with Shell commands
type ShellHelper struct {
verbose bool
}
// NewShellHelper creates a new ShellHelper!
func NewShellHelper() *ShellHelper {
return &ShellHelper{}
}
// SetVerbose sets the verbose flag
func (sh *ShellHelper) SetVerbose() {
sh.verbose = true
}
// Run the given command
func (sh *ShellHelper) Run(command string, vars ...string) (stdout, stderr string, err error) {
cmd := exec.Command(command, vars...)
cmd.Env = append(os.Environ(), "GO111MODULE=on")
if !sh.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
return
}
// RunInDirectory runs the given command in the given directory
func (sh *ShellHelper) RunInDirectory(dir string, command string, vars ...string) (stdout, stderr string, err error) {
cmd := exec.Command(command, vars...)
cmd.Dir = dir
cmd.Env = append(os.Environ(), "GO111MODULE=on")
if !sh.verbose {
var stdo, stde bytes.Buffer
cmd.Stdout = &stdo
cmd.Stderr = &stde
err = cmd.Run()
stdout = string(stdo.Bytes())
stderr = string(stde.Bytes())
} else {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Run()
}
return
}

317
cmd/system.go Normal file
View file

@ -0,0 +1,317 @@
package cmd
import (
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strconv"
"time"
)
// SystemHelper - Defines everything related to the system
type SystemHelper struct {
log *Logger
fs *FSHelper
configFilename string
homeDir string
wailsSystemDir string
wailsSystemConfig string
}
// NewSystemHelper - Creates a new System Helper
func NewSystemHelper() *SystemHelper {
result := &SystemHelper{
fs: NewFSHelper(),
log: NewLogger(),
configFilename: "wails.json",
}
result.setSystemDirs()
return result
}
// Internal
// setSystemDirs calculates the system directories it is interested in
func (s *SystemHelper) setSystemDirs() {
var err error
s.homeDir, err = os.UserHomeDir()
if err != nil {
log.Fatal("Cannot find home directory! Please file a bug report!")
}
// TODO: A better config system
s.wailsSystemDir = filepath.Join(s.homeDir, ".wails")
s.wailsSystemConfig = filepath.Join(s.wailsSystemDir, s.configFilename)
}
// ConfigFileExists - Returns true if it does!
func (s *SystemHelper) ConfigFileExists() bool {
return s.fs.FileExists(s.wailsSystemConfig)
}
// SystemDirExists - Returns true if it does!
func (s *SystemHelper) systemDirExists() bool {
return s.fs.DirExists(s.wailsSystemDir)
}
// LoadConfig attempts to load the Wails system config
func (s *SystemHelper) LoadConfig() (*SystemConfig, error) {
return NewSystemConfig(s.wailsSystemConfig)
}
// ConfigFileIsValid checks if the config file is valid
func (s *SystemHelper) ConfigFileIsValid() bool {
_, err := NewSystemConfig(s.wailsSystemConfig)
return err == nil
}
// GetAuthor returns a formatted string of the user's name and email
func (s *SystemHelper) GetAuthor() (string, error) {
var config *SystemConfig
config, err := s.LoadConfig()
if err != nil {
return "", err
}
return fmt.Sprintf("%s <%s>", config.Name, config.Email), nil
}
// BackupConfig attempts to backup the system config file
func (s *SystemHelper) BackupConfig() (string, error) {
now := strconv.FormatInt(time.Now().UTC().UnixNano(), 10)
backupFilename := s.wailsSystemConfig + "." + now
err := s.fs.CopyFile(s.wailsSystemConfig, backupFilename)
if err != nil {
return "", err
}
return backupFilename, nil
}
func (s *SystemHelper) setup() error {
systemConfig := make(map[string]string)
// Try to load current values - ignore errors
config, _ := s.LoadConfig()
if config.Name != "" {
systemConfig["name"] = PromptRequired("What is your name", config.Name)
} else if n, err := getGitConfigValue("user.name"); err == nil && n != "" {
systemConfig["name"] = PromptRequired("What is your name", n)
} else {
systemConfig["name"] = PromptRequired("What is your name")
}
if config.Email != "" {
systemConfig["email"] = PromptRequired("What is your email address", config.Email)
} else if e, err := getGitConfigValue("user.email"); err == nil && e != "" {
systemConfig["email"] = PromptRequired("What is your email address", e)
} else {
systemConfig["email"] = PromptRequired("What is your email address")
}
// Create the directory
err := s.fs.MkDirs(s.wailsSystemDir)
if err != nil {
return err
}
// Save
configData, err := json.Marshal(&systemConfig)
if err != nil {
return err
}
err = os.WriteFile(s.wailsSystemConfig, configData, 0755)
if err != nil {
return err
}
fmt.Println()
s.log.White("Wails config saved to: " + s.wailsSystemConfig)
s.log.White("Feel free to customise these settings.")
fmt.Println()
return nil
}
const introText = `
Wails is a lightweight framework for creating web-like desktop apps in Go.
I'll need to ask you a few questions so I can fill in your project templates and then I will try and see if you have the correct dependencies installed. If you don't have the right tools installed, I'll try and suggest how to install them.
`
// CheckInitialised checks if the system has been set up
// and if not, runs setup
func (s *SystemHelper) CheckInitialised() error {
if !s.systemDirExists() {
s.log.Yellow("System not initialised. Running setup.")
return s.setup()
}
return nil
}
// Initialise attempts to set up the Wails system.
// An error is returns if there is a problem
func (s *SystemHelper) Initialise() error {
// System dir doesn't exist
if !s.systemDirExists() {
s.log.Green("Welcome to Wails!")
s.log.Green(introText)
return s.setup()
}
// Config doesn't exist
if !s.ConfigFileExists() {
s.log.Green("Looks like the system config is missing.")
s.log.Green("To get you back on track, I'll need to ask you a few things...")
return s.setup()
}
// Config exists but isn't valid.
if !s.ConfigFileIsValid() {
s.log.Green("Looks like the system config got corrupted.")
backupFile, err := s.BackupConfig()
if err != nil {
s.log.Green("I tried to backup your config file but got this error: %s", err.Error())
} else {
s.log.Green("Just in case you needed it, I backed up your config file here: %s", backupFile)
}
s.log.Green("To get you back on track, I'll need to ask you a few things...")
return s.setup()
}
return s.setup()
}
// SystemConfig - Defines system wide configuration data
type SystemConfig struct {
Name string `json:"name"`
Email string `json:"email"`
}
// NewSystemConfig - Creates a new SystemConfig helper object
func NewSystemConfig(filename string) (*SystemConfig, error) {
result := &SystemConfig{}
err := result.load(filename)
return result, err
}
// Save - Saves the system config to the given filename
func (sc *SystemConfig) Save(filename string) error {
// Convert config to JSON string
theJSON, err := json.MarshalIndent(sc, "", " ")
if err != nil {
return err
}
// Write it out to the config file
return os.WriteFile(filename, theJSON, 0644)
}
func (sc *SystemConfig) load(filename string) error {
configData, err := os.ReadFile(filename)
if err != nil {
return err
}
// Load and unmarshall!
err = json.Unmarshal(configData, &sc)
if err != nil {
return err
}
return nil
}
// CheckDependenciesSilent checks for dependencies but
// only outputs if there's an error
func CheckDependenciesSilent(logger *Logger) (bool, error) {
logger.SetErrorOnly(true)
result, err := CheckDependencies(logger)
logger.SetErrorOnly(false)
return result, err
}
// CheckDependencies will look for Wails dependencies on the system
// Errors are reported in error and the bool return value is whether
// the dependencies are all installed.
func CheckDependencies(logger *Logger) (bool, error) {
switch runtime.GOOS {
case "darwin":
logger.Yellow("Detected Platform: OSX")
case "windows":
logger.Yellow("Detected Platform: Windows")
case "linux":
logger.Yellow("Detected Platform: Linux")
default:
return false, fmt.Errorf("Platform %s is currently not supported", runtime.GOOS)
}
logger.Yellow("Checking for prerequisites...")
// Check we have a cgo capable environment
requiredPrograms, err := GetRequiredPrograms()
if err != nil {
return false, nil
}
errors := false
programHelper := NewProgramHelper()
for _, program := range *requiredPrograms {
bin := programHelper.FindProgram(program.Name)
if bin == nil {
errors = true
logger.Error("Program '%s' not found. %s", program.Name, program.Help)
} else {
logger.Green("Program '%s' found: %s", program.Name, bin.Path)
}
}
// Linux has library deps
if runtime.GOOS == "linux" {
// Check library prerequisites
requiredLibraries, err := GetRequiredLibraries()
if err != nil {
return false, err
}
var libraryChecker CheckPkgInstalled
distroInfo := GetLinuxDistroInfo()
switch distroInfo.Distribution {
case Ubuntu, Debian, Zorin, Parrot, Linuxmint, Elementary, Kali, Neon, Deepin, Raspbian, PopOS, Uos:
libraryChecker = DpkgInstalled
case Arch, ArcoLinux, ArchLabs, Ctlos, Manjaro, ManjaroARM, EndeavourOS, ArtixLinux:
libraryChecker = PacmanInstalled
case CentOS, Fedora, Tumbleweed, Leap, RHEL:
libraryChecker = RpmInstalled
case Gentoo:
libraryChecker = EqueryInstalled
case VoidLinux:
libraryChecker = XbpsInstalled
case Solus:
libraryChecker = EOpkgInstalled
case Crux:
libraryChecker = PrtGetInstalled
case NixOS:
libraryChecker = NixEnvInstalled
default:
return false, RequestSupportForDistribution(distroInfo)
}
for _, library := range *requiredLibraries {
installed, err := libraryChecker(library.Name)
if err != nil {
return false, err
}
if !installed {
errors = true
logger.Error("Library '%s' not found. %s", library.Name, library.Help)
} else {
logger.Green("Library '%s' installed.", library.Name)
}
}
}
logger.White("")
return !errors, err
}

270
cmd/templates.go Normal file
View file

@ -0,0 +1,270 @@
package cmd
import (
"bytes"
"encoding/json"
"fmt"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"text/template"
"github.com/kennygrant/sanitize"
"github.com/leaanthony/slicer"
)
// TemplateMetadata holds all the metadata for a Wails template
type TemplateMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
ShortDescription string `json:"shortdescription"`
Description string `json:"description"`
Install string `json:"install"`
Build string `json:"build"`
Author string `json:"author"`
Created string `json:"created"`
FrontendDir string `json:"frontenddir"`
Serve string `json:"serve"`
Bridge string `json:"bridge"`
WailsDir string `json:"wailsdir"`
TemplateDependencies []*TemplateDependency `json:"dependencies,omitempty"`
// List of platforms that this template is supported on.
// No value means all platforms. A platform name is the same string
// as `runtime.GOOS` will return, eg: "darwin". NOTE: This is
// case sensitive.
Platforms []string `json:"platforms,omitempty"`
}
// PlatformSupported returns true if this template supports the
// currently running platform
func (m *TemplateMetadata) PlatformSupported() bool {
// Default is all platforms supported
if len(m.Platforms) == 0 {
return true
}
// Check that the platform is in the list
platformsSupported := slicer.String(m.Platforms)
return platformsSupported.Contains(runtime.GOOS)
}
// TemplateDependency defines a binary dependency for the template
// EG: ng for angular
type TemplateDependency struct {
Bin string `json:"bin"`
Help string `json:"help"`
}
// TemplateDetails holds information about a specific template
type TemplateDetails struct {
Name string
Path string
Metadata *TemplateMetadata
fs *FSHelper
}
// TemplateHelper is a utility object to help with processing templates
type TemplateHelper struct {
templateDir *Dir
fs *FSHelper
metadataFilename string
}
// NewTemplateHelper creates a new template helper
func NewTemplateHelper() *TemplateHelper {
templateDir, err := fs.LocalDir("./templates")
if err != nil {
log.Fatal("Unable to find the template directory. Please reinstall Wails.")
}
return &TemplateHelper{
templateDir: templateDir,
metadataFilename: "template.json",
}
}
// IsValidTemplate returns true if the given template name resides on disk
func (t *TemplateHelper) IsValidTemplate(templateName string) bool {
pathToTemplate := filepath.Join(t.templateDir.fullPath, templateName)
return t.fs.DirExists(pathToTemplate)
}
// SanitizeFilename sanitizes the given string to make a valid filename
func (t *TemplateHelper) SanitizeFilename(name string) string {
return sanitize.Name(name)
}
// CreateNewTemplate creates a new template based on the given directory name and string
func (t *TemplateHelper) CreateNewTemplate(dirname string, details *TemplateMetadata) (string, error) {
// Check if this template has already been created
if t.IsValidTemplate(dirname) {
return "", fmt.Errorf("cannot create template in directory '%s' - already exists", dirname)
}
targetDir := filepath.Join(t.templateDir.fullPath, dirname)
err := t.fs.MkDir(targetDir)
if err != nil {
return "", err
}
targetMetadata := filepath.Join(targetDir, t.metadataFilename)
err = t.fs.SaveAsJSON(details, targetMetadata)
return targetDir, err
}
// LoadMetadata loads the template's 'metadata.json' file
func (t *TemplateHelper) LoadMetadata(dir string) (*TemplateMetadata, error) {
templateFile := filepath.Join(dir, t.metadataFilename)
result := &TemplateMetadata{}
if !t.fs.FileExists(templateFile) {
return nil, nil
}
rawJSON, err := os.ReadFile(templateFile)
if err != nil {
return nil, err
}
err = json.Unmarshal(rawJSON, &result)
return result, err
}
// GetTemplateDetails returns a map of Template structs containing details
// of the found templates
func (t *TemplateHelper) GetTemplateDetails() (map[string]*TemplateDetails, error) {
// Get the subdirectory details
templateDirs, err := t.templateDir.GetSubdirs()
if err != nil {
return nil, err
}
result := make(map[string]*TemplateDetails)
for name, dir := range templateDirs {
result[name] = &TemplateDetails{
Path: dir,
}
metadata, err := t.LoadMetadata(dir)
if err != nil {
return nil, err
}
result[name].Metadata = metadata
if metadata.Name != "" {
result[name].Name = metadata.Name
} else {
// Ignore bad templates?
result[name] = nil
}
}
return result, nil
}
// GetTemplateFilenames returns all the filenames of the given template
func (t *TemplateHelper) GetTemplateFilenames(template *TemplateDetails) (*slicer.StringSlicer, error) {
// Get the subdirectory details
templateDir, err := t.fs.Directory(template.Path)
if err != nil {
return nil, err
}
return templateDir.GetAllFilenames()
}
// InstallTemplate installs the template given in the project options to the
// project path given
func (t *TemplateHelper) InstallTemplate(projectPath string, projectOptions *ProjectOptions) error {
// Check dependencies before installing
dependencies := projectOptions.selectedTemplate.Metadata.TemplateDependencies
if dependencies != nil {
programHelper := NewProgramHelper()
logger := NewLogger()
errors := []string{}
for _, dep := range dependencies {
program := programHelper.FindProgram(dep.Bin)
if program == nil {
errors = append(errors, dep.Help)
}
}
if len(errors) > 0 {
mainError := "template dependencies not installed"
if len(errors) == 1 {
mainError = errors[0]
} else {
for _, error := range errors {
logger.Red(error)
}
}
return fmt.Errorf(mainError)
}
}
// Get template files
templateFilenames, err := t.GetTemplateFilenames(projectOptions.selectedTemplate)
if err != nil {
return err
}
templatePath := projectOptions.selectedTemplate.Path
// Save the version
projectOptions.WailsVersion = Version
templateJSONFilename := filepath.Join(templatePath, t.metadataFilename)
templateFiles := templateFilenames.Filter(func(filename string) bool {
filename = filepath.FromSlash(filename)
return strings.HasPrefix(filename, templatePath) && filename != templateJSONFilename
})
templateFiles.Each(func(templateFile string) {
// Setup filenames
relativeFilename := strings.TrimPrefix(templateFile, templatePath)[1:]
targetFilename, err := filepath.Abs(filepath.Join(projectOptions.OutputDirectory, relativeFilename))
if err != nil {
return
}
filedata, err := t.fs.LoadAsBytes(templateFile)
if err != nil {
return
}
// If file is a template, process it
if strings.HasSuffix(templateFile, ".template") {
templateData := string(filedata)
tmpl := template.New(templateFile)
tmpl.Parse(templateData)
var tpl bytes.Buffer
err = tmpl.Execute(&tpl, projectOptions)
if err != nil {
return
}
// Remove template suffix
targetFilename = strings.TrimSuffix(targetFilename, ".template")
// Set the filedata to the template result
filedata = tpl.Bytes()
}
// Normal file, just copy it
err = fs.CreateFile(targetFilename, filedata)
if err != nil {
return
}
})
if err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,13 @@
# Editor configuration, see https://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
max_line_length = off
trim_trailing_whitespace = false

View file

@ -0,0 +1,47 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# Only exists if Bazel was run
/bazel-out
# dependencies
/node_modules
# profiling files
chrome-profiler-events.json
speed-measure-plugin.json
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
yarn-error.log
testem.log
/typings
# System Files
.DS_Store
Thumbs.db
.editorcinfig

View file

@ -0,0 +1,27 @@
# MyApp
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 8.0.3.
## Development server
Run `npx ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
## Code scaffolding
Run `npx ng generate component component-name` to generate a new component. You can also use `npx ng generate directive|pipe|service|class|guard|interface|enum|module`.
## Build
Run `npx ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
## Running unit tests
Run `npx ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
## Running end-to-end tests
Run `npx ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
## Further help
To get more help on the Angular CLI use `npx ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).

View file

@ -0,0 +1,121 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"my-app": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "ngx-build-plus:browser",
"options": {
"outputPath": "dist/my-app",
"index": "src/index.html",
"main": "src/main.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.app.json",
"aot": false,
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
"vendorChunk": false,
"buildOptimizer": true,
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
}
]
}
}
},
"serve": {
"builder": "ngx-build-plus:dev-server",
"options": {
"browserTarget": "my-app:build"
},
"configurations": {
"production": {
"browserTarget": "my-app:build:production"
}
}
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "my-app:build"
}
},
"test": {
"builder": "ngx-build-plus:karma",
"options": {
"main": "src/test.ts",
"polyfills": "src/polyfills.ts",
"tsConfig": "tsconfig.spec.json",
"karmaConfig": "karma.conf.js",
"assets": [
"src/favicon.ico",
"src/assets"
],
"styles": [
"src/styles.css"
],
"scripts": []
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": [
"tsconfig.app.json",
"tsconfig.spec.json",
"e2e/tsconfig.json"
],
"exclude": [
"**/node_modules/**"
]
}
},
"e2e": {
"builder": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "e2e/protractor.conf.js",
"devServerTarget": "my-app:serve"
},
"configurations": {
"production": {
"devServerTarget": "my-app:serve:production"
}
}
}
}
}
},
"defaultProject": "my-app"
}

View file

@ -0,0 +1,12 @@
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
# You can see what browsers were selected by your queries by running:
# npx browserslist
> 0.5%
last 2 versions
Firefox ESR
not dead
IE 9-11 # For IE 9-11 support, remove 'not'.

View file

@ -0,0 +1,32 @@
// @ts-check
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
const { SpecReporter } = require('jasmine-spec-reporter');
/**
* @type { import("protractor").Config }
*/
exports.config = {
allScriptsTimeout: 11000,
specs: [
'./src/**/*.e2e-spec.ts'
],
capabilities: {
'browserName': 'chrome'
},
directConnect: true,
baseUrl: 'http://localhost:4200/',
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 30000,
print: function() {}
},
onPrepare() {
require('ts-node').register({
project: require('path').join(__dirname, './tsconfig.json')
});
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
}
};

View file

@ -0,0 +1,23 @@
import { AppPage } from './app.po';
import { browser, logging } from 'protractor';
describe('workspace-project App', () => {
let page: AppPage;
beforeEach(() => {
page = new AppPage();
});
it('should display welcome message', () => {
page.navigateTo();
expect(page.getTitleText()).toEqual('Welcome to my-app!');
});
afterEach(async () => {
// Assert that there are no errors emitted from the browser
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
expect(logs).not.toContain(jasmine.objectContaining({
level: logging.Level.SEVERE,
} as logging.Entry));
});
});

View file

@ -0,0 +1,11 @@
import { browser, by, element } from 'protractor';
export class AppPage {
navigateTo() {
return browser.get(browser.baseUrl) as Promise<any>;
}
getTitleText() {
return element(by.css('app-root h1')).getText() as Promise<string>;
}
}

View file

@ -0,0 +1,13 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "../out-tsc/e2e",
"module": "commonjs",
"target": "es5",
"types": [
"jasmine",
"jasminewd2",
"node"
]
}
}

View file

@ -0,0 +1,32 @@
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html
module.exports = function (config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage-istanbul-reporter'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false // leave Jasmine Spec Runner output visible in browser
},
coverageIstanbulReporter: {
dir: require('path').join(__dirname, './coverage/my-app'),
reports: ['html', 'lcovonly', 'text-summary'],
fixWebpackSourcePaths: true
},
reporters: ['progress', 'kjhtml'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};

View file

@ -0,0 +1,52 @@
{
"name": "my-app",
"version": "0.0.0",
"scripts": {
"ng": "npx ng",
"serve": "npx ng serve --poll=2000 --host=0.0.0.0",
"build": "npx ng build --single-bundle true --output-hashing none --prod --bundle-styles false",
"test": "npx ng test",
"lint": "npx ng lint",
"e2e": "npx ng e2e"
},
"private": true,
"dependencies": {
"@angular/animations": "^8.0.2",
"@angular/cdk": "^8.0.1",
"@angular/common": "~8.0.1",
"@angular/compiler": "~8.0.1",
"@angular/core": "~8.0.1",
"@angular/forms": "~8.0.1",
"@angular/material": "^8.0.1",
"@angular/platform-browser": "~8.0.1",
"@angular/platform-browser-dynamic": "~8.0.1",
"@angular/router": "~8.0.1",
"@wailsapp/runtime": "^1.0.0",
"core-js": "^3.4.4",
"ngx-build-plus": "^8.0.3",
"rxjs": "~6.4.0",
"tslib": "^1.9.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.800.0",
"@angular/cli": "~8.0.3",
"@angular/compiler-cli": "~8.0.1",
"@angular/language-service": "~8.0.1",
"@types/node": "~8.9.4",
"@types/jasmine": "~3.3.8",
"@types/jasminewd2": "~2.0.3",
"codelyzer": "^5.0.0",
"jasmine-core": "~3.4.0",
"jasmine-spec-reporter": "~4.2.1",
"karma": "~4.1.0",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-jasmine": "~2.0.1",
"karma-jasmine-html-reporter": "^1.4.0",
"protractor": "~5.4.0",
"ts-node": "~7.0.0",
"tslint": "~5.15.0",
"typescript": "~3.4.3"
}
}

View file

@ -0,0 +1,13 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forRoot(routes,{useHash:true})
],
exports: [RouterModule]
})
export class AppRoutingModule { }

View file

@ -0,0 +1,14 @@
<!--The content below is only a placeholder and can be replaced.-->
<div style="text-align:center">
<h1>
Welcome to {{ title }}!
</h1>
<img width="300" alt="Angular Logo"
src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
<br />
<button (click)="onClickMe()">Hello</button>
<p>{{clickMessage}}</p>
</div>
<router-outlet></router-outlet>

View file

@ -0,0 +1,35 @@
import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule
],
declarations: [
AppComponent
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'my-app'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('my-app');
});
it('should render title in a h1 tag', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('Welcome to my-app!');
});
});

View file

@ -0,0 +1,19 @@
import { Component } from '@angular/core';
@Component({
selector: '[id="app"]',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'my-app';
clickMessage = '';
onClickMe() {
// @ts-ignore
window.backend.basic().then(result =>
this.clickMessage = result
);
}
}

View file

@ -0,0 +1,20 @@
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { APP_BASE_HREF } from '@angular/common';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [{provide: APP_BASE_HREF, useValue : '/' }],
bootstrap: [AppComponent]
})
export class AppModule { }

View file

@ -0,0 +1,3 @@
export const environment = {
production: true
};

Some files were not shown because too many files have changed in this diff Show more