Recently, I created a CLI for windows. I learned about some TIPS. Let's describe that in this article. Sample code exists in https://github.com/y-yagi/cli-for-windows
"GUI binary" or "console binary"
By default, Go build a binary as "console binary". This means a window console is kept showing when a binary is executed. It is good for applications that will finish immediately. But that's not good for some kind of applications(e.g. GUI application, HTTP server).
In the case of such applications, we can build binary as "GUI binary". In "GUI binary", a window console doesn't show when a binary is executed.
In order to build with "GUI binary", you need to specify "-H windowsgui" to ldflags.
On Windows, -H windowsgui writes a "GUI binary" instead of a "console binary."
$ go build -ldflags "-H windowsgui"
See also: https://github.com/golang/go/blob/3b2a578166bdedd94110698c971ba8990771eb89/src/cmd/link/doc.go#L28
Embed icon to a binary file
Maybe you want to include an icon in your CLI. Go build binary as a Portable Executable
format. Portable Executable format has a section for resources(rsrc section). We need to add an icon to that section.
When embed data to Go binary, need to prepare syso
file. There is a tool called rsrc that will generate syso
file for embedding in the rsrc section, so let's use that.
$ go get github.com/akavel/rsrc
$ rsrc -ico your-icon.ico -arch=amd64
You need to prepare syso
files per architecture. syso
file will link automatically when run go build
. So we don't need to specify options or files to go build
.
See also: GcToolchainTricks · golang/go Wiki
Notification Area
If you choose "GUI binary" applications, maybe we need to consider how the exit of applications.
In the case of "console binary", if want to exit, simplify close the console. But in the case of "GUI binary", we can't do that.
Of course, we can exit applications via the Task Manager. But maybe it difficult for people who unfamiliar with Windows.
So let's consider to use "Notification Area"(or called "System Tray").
The notification area is located at the right end of the taskbar, it can be operated to applications by GUI.
Of course, already exists that library for using Notification Area.
getlantern/systray
Here's an example code with systray
.
func main() {
systray.Run(onReady, onExit) // (1)
return
}
func onReady() {
systray.SetIcon(icon) // (2)
systray.SetTitle("My CLI")
systray.SetTooltip("My CLI")
mQuit := systray.AddMenuItem("Quit", "Quit") // (3)
<-mQuit.ClickedCh
systray.Quit()
}
func onExit() {
// clean up
}
(1) systray.Run
initializes GUI and starts the event loop. Also, you can pass two functions that run when applications starting and exiting.
(2) systray.SetIcon
accept to array bytes of icon data. This means you need to prepare array bytes of an icon. 2goarray can use in this case. 2goarray
can encode files into array bytes.
$ go get github.com/cratonica/2goarray
$ 2goarray icon main < icon.png > icon.go
(3) systray.AddMenuItem
add a menu item to an application. In this example, add a menu item that quit an application.
This example shows as follows on Windows.
CI
Of course, we need to consider the CI. Before, we have little choice for CI. But now we have some choices. For example, AppVeyor, CircleCI, and GitHub Actions.
In my personal opinion, if you are using GitHub, I recommend GitHub Actions. Because it can use easily and don't' need to create an extra account.
An example of GitHub Actions is the following. It needs to add a file to .github/workflows/
directory for use(e.g. .github/workflows/ci.yml
).
name: CI
on: [push]
jobs:
build:
name: Build
runs-on: windows-latest
steps:
- name: Set up Go # (1)
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Check out code # (2)
uses: actions/checkout@v2
- name: Run test (3)
run: |
go test ./...
env:
GO111MODULE: on
(1) Set up Go environment by setup-go action.
(2) Checks-out my repository by checkout action.
(3) Run tests.
That's it!
If you want to run golangci-lint
in CI, you can use golangci-lint-action same as other actions.
Release
Finally, let's consider the management of release binaries. If you are using GitHub, I think GitHub Releases is good for that. It can manage source and binaries in the same place.
GitHub Releases are based on Git tags. So I wanted to upload binaries to GitHub Releases when push gi tags. We can use GitHub Actions also this case. Here is an example of a setting.
name: Release
on:
push:
tags:
- 'v*' # (1)
jobs:
windows:
runs-on: windows-latest
steps:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Run release # (2)
uses: goreleaser/goreleaser-action@v2
with:
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
(1) Restrict that only run when git tag started with v
(e.g. v1.0.0
).
(2) Use GoReleaser action for upload binaries to GitHub Releases.
GoReleaser
settings as follows.
builds:
- binary: "MY CLI"
goos:
- windows
ldflags: -H windowsgui
goarch:
- amd64
- 386
archives:
-
format: zip
release:
github:
owner: y-yagi
name: cli-for-windows
You need to specify github
section under the release
section` for upload binaries to GitHub Releases.
Top comments (0)