Feature 8058: Added resource.syso metadata file (#10783)
Addresses https://github.com/fleetdm/fleet/issues/8058, https://github.com/fleetdm/fleet/issues/11012 and https://github.com/fleetdm/fleet/issues/11013 This PR adds a new VERSIONINFO metadata file using the https://github.com/josephspurrier/goversioninfo library.
This commit is contained in:
@@ -89,3 +89,9 @@ macoffice_rel_notes/
|
||||
|
||||
# IDE
|
||||
.vscode
|
||||
|
||||
# residual files when running the build-windows tool
|
||||
orbit/cmd/desktop/manifest.xml
|
||||
orbit/cmd/desktop/resource.syso
|
||||
orbit/cmd/orbit/manifest.xml
|
||||
orbit/cmd/orbit/resource.syso
|
||||
|
||||
@@ -394,13 +394,17 @@ endif
|
||||
FLEET_DESKTOP_VERSION ?= unknown
|
||||
|
||||
# Build desktop executable for Windows.
|
||||
# This generates desktop executable for Windows that includes versioninfo binary properties
|
||||
# These properties can be displayed when right-click on the binary in Windows Explorer.
|
||||
# See: https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
|
||||
# To sign this binary with a certificate, use signtool.exe or osslsigncode tool
|
||||
#
|
||||
# Usage:
|
||||
# FLEET_DESKTOP_VERSION=0.0.1 make desktop-windows
|
||||
#
|
||||
# Output: fleet-desktop.exe
|
||||
desktop-windows:
|
||||
GOOS=windows GOARCH=amd64 go build -ldflags "-H=windowsgui -X=main.version=$(FLEET_DESKTOP_VERSION)" -o fleet-desktop.exe ./orbit/cmd/desktop
|
||||
go run ./orbit/tools/build/build-windows.go -version $(FLEET_DESKTOP_VERSION) -input ./orbit/cmd/desktop -output fleet-desktop.exe
|
||||
|
||||
# Build desktop executable for Linux.
|
||||
#
|
||||
@@ -423,6 +427,19 @@ desktop-linux:
|
||||
tar czf desktop.tar.gz fleet-desktop && \
|
||||
rm -r fleet-desktop"
|
||||
|
||||
# Build orbit executable for Windows.
|
||||
# This generates orbit executable for Windows that includes versioninfo binary properties
|
||||
# These properties can be displayed when right-click on the binary in Windows Explorer.
|
||||
# See: https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
|
||||
# To sign this binary with a certificate, use signtool.exe or osslsigncode tool
|
||||
#
|
||||
# Usage:
|
||||
# ORBIT_VERSION=0.0.1 make orbit-windows
|
||||
#
|
||||
# Output: orbit.exe
|
||||
orbit-windows:
|
||||
go run ./orbit/tools/build/build-windows.go -version $(ORBIT_VERSION) -input ./orbit/cmd/orbit -output orbit.exe
|
||||
|
||||
# db-replica-setup setups one main and one read replica MySQL instance for dev/testing.
|
||||
# - Assumes the docker containers are already running (tools/mysql-replica-testing/docker-compose.yml)
|
||||
# - MySQL instance listening on 3308 is the main instance.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
- Added version info metadata file to windows installer.
|
||||
@@ -149,6 +149,7 @@ require (
|
||||
github.com/RobotsAndPencils/buford v0.14.0 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/agnivade/levenshtein v1.1.1 // indirect
|
||||
github.com/akavel/rsrc v0.10.2 // indirect
|
||||
github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/antchfx/xpath v1.2.2 // indirect
|
||||
@@ -241,6 +242,7 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/josephspurrier/goversioninfo v1.4.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.11 // indirect
|
||||
github.com/lib/pq v1.10.6 // indirect
|
||||
|
||||
@@ -192,6 +192,8 @@ github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki
|
||||
github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0=
|
||||
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
|
||||
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
|
||||
github.com/akavel/rsrc v0.10.2 h1:Zxm8V5eI1hW4gGaYsJQUhxpjkENuG91ki8B4zCrvEsw=
|
||||
github.com/akavel/rsrc v0.10.2/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||
github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725 h1:NjwIgLQlD46o79bheVG4SCdRnnOz4XtgUN1WABX5DLA=
|
||||
github.com/alecthomas/jsonschema v0.0.0-20211022214203-8b29eab41725/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60=
|
||||
@@ -839,6 +841,8 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josephspurrier/goversioninfo v1.4.0 h1:Puhl12NSHUSALHSuzYwPYQkqa2E1+7SrtAPJorKK0C8=
|
||||
github.com/josephspurrier/goversioninfo v1.4.0/go.mod h1:JWzv5rKQr+MmW+LvM412ToT/IkYDZjaclF2pKDss8IY=
|
||||
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
* Added version information and icon on orbit and fleet-desktop binaries
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 71 KiB |
@@ -16,6 +16,8 @@ builds:
|
||||
- -X github.com/fleetdm/fleet/v4/orbit/pkg/build.Version={{.Version}}
|
||||
- -X github.com/fleetdm/fleet/v4/orbit/pkg/build.Commit={{.Commit}}
|
||||
- -X github.com/fleetdm/fleet/v4/orbit/pkg/build.Date={{.Date}}
|
||||
hooks:
|
||||
pre: "go run ./orbit/tools/build/build-windows.go -version {{.Version}} -input ./orbit/cmd/orbit -resource=true"
|
||||
|
||||
archives:
|
||||
- id: orbit
|
||||
|
||||
@@ -3,18 +3,23 @@ package packaging
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/constant"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/packaging/wix"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/update"
|
||||
"github.com/fleetdm/fleet/v4/pkg/file"
|
||||
"github.com/fleetdm/fleet/v4/pkg/secure"
|
||||
"github.com/josephspurrier/goversioninfo"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
@@ -123,6 +128,10 @@ func BuildMSI(opt Options) (string, error) {
|
||||
return "", fmt.Errorf("write powershell installer utils file: %w", err)
|
||||
}
|
||||
|
||||
if err := writeResourceSyso(opt, orbitRoot); err != nil {
|
||||
return "", fmt.Errorf("write VERSIONINFO: %w", err)
|
||||
}
|
||||
|
||||
if err := writeWixFile(opt, tmpDir); err != nil {
|
||||
return "", fmt.Errorf("write wix file: %w", err)
|
||||
}
|
||||
@@ -225,3 +234,148 @@ func writePowershellInstallerUtilsFile(opt Options, rootPath string) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// writeManifestXML creates the manifest.xml file used when generating the 'resource.syso' metadata
|
||||
// (see writeResourceSyso). Returns the path of the newly created file.
|
||||
func writeManifestXML(vParts []string, orbitPath string) (string, error) {
|
||||
filePath := filepath.Join(orbitPath, "manifest.xml")
|
||||
|
||||
tmplOpts := struct {
|
||||
Version string
|
||||
}{
|
||||
Version: strings.Join(vParts, "."),
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
if err := ManifestXMLTemplate.Execute(&contents, tmplOpts); err != nil {
|
||||
return "", fmt.Errorf("parsing manifest.xml template: %w", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filePath, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
||||
return "", fmt.Errorf("writing manifest.xml file: %w", err)
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// createVersionInfo returns a VersionInfo struct pointer to be used to generate the 'resource.syso'
|
||||
// metadata file (see writeResourceSyso).
|
||||
func createVersionInfo(vParts []string, manifestPath string) (*goversioninfo.VersionInfo, error) {
|
||||
vIntParts := make([]int, 0, len(vParts))
|
||||
for _, p := range vParts {
|
||||
v, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing version part %s: %w", p, err)
|
||||
}
|
||||
vIntParts = append(vIntParts, v)
|
||||
}
|
||||
version := strings.Join(vParts, ".")
|
||||
copyright := fmt.Sprintf("%d Fleet Device Management Inc.", time.Now().Year())
|
||||
|
||||
// Taken from https://github.com/josephspurrier/goversioninfo/blob/master/testdata/resource/versioninfo.json
|
||||
langID, err := strconv.ParseUint("0409", 16, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid LangID")
|
||||
}
|
||||
// Taken from https://github.com/josephspurrier/goversioninfo/blob/master/testdata/resource/versioninfo.json
|
||||
charsetID, err := strconv.ParseUint("04B0", 16, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid charsetID")
|
||||
}
|
||||
|
||||
result := goversioninfo.VersionInfo{
|
||||
FixedFileInfo: goversioninfo.FixedFileInfo{
|
||||
FileVersion: goversioninfo.FileVersion{
|
||||
Major: vIntParts[0],
|
||||
Minor: vIntParts[1],
|
||||
Patch: vIntParts[2],
|
||||
Build: vIntParts[3],
|
||||
},
|
||||
ProductVersion: goversioninfo.FileVersion{
|
||||
Major: vIntParts[0],
|
||||
Minor: vIntParts[1],
|
||||
Patch: vIntParts[2],
|
||||
Build: vIntParts[3],
|
||||
},
|
||||
FileFlagsMask: "3f",
|
||||
FileFlags: "00",
|
||||
FileOS: "040004",
|
||||
FileType: "01",
|
||||
FileSubType: "00",
|
||||
},
|
||||
StringFileInfo: goversioninfo.StringFileInfo{
|
||||
Comments: "Fleet osquery",
|
||||
CompanyName: "Fleet Device Management (fleetdm.com)",
|
||||
FileDescription: "Fleet osquery installer",
|
||||
FileVersion: version,
|
||||
InternalName: "",
|
||||
LegalCopyright: copyright,
|
||||
LegalTrademarks: "",
|
||||
OriginalFilename: "",
|
||||
PrivateBuild: "",
|
||||
ProductName: "Fleet osquery",
|
||||
ProductVersion: version,
|
||||
SpecialBuild: "",
|
||||
},
|
||||
VarFileInfo: goversioninfo.VarFileInfo{
|
||||
Translation: goversioninfo.Translation{
|
||||
LangID: goversioninfo.LangID(langID),
|
||||
CharsetID: goversioninfo.CharsetID(charsetID),
|
||||
},
|
||||
},
|
||||
IconPath: "",
|
||||
ManifestPath: manifestPath,
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// SanitizeVersion returns the version parts (Major, Minor, Patch and Build), filling the Build part
|
||||
// with '0' if missing. Will error out if the version string is missing the Major, Minor or
|
||||
// Patch part(s).
|
||||
func SanitizeVersion(version string) ([]string, error) {
|
||||
vParts := strings.Split(version, ".")
|
||||
if len(vParts) < 3 {
|
||||
return nil, errors.New("invalid version string")
|
||||
}
|
||||
|
||||
if len(vParts) < 4 {
|
||||
vParts = append(vParts, "0")
|
||||
}
|
||||
|
||||
return vParts[:4], nil
|
||||
}
|
||||
|
||||
// writeResourceSyso creates the 'resource.syso' metadata file which contains the required Microsoft
|
||||
// Windows Version Information
|
||||
func writeResourceSyso(opt Options, orbitPath string) error {
|
||||
if err := secure.MkdirAll(orbitPath, constant.DefaultDirMode); err != nil {
|
||||
return fmt.Errorf("mkdir: %w", err)
|
||||
}
|
||||
|
||||
vParts, err := SanitizeVersion(opt.Version)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid version %s: %w", opt.Version, err)
|
||||
}
|
||||
|
||||
manifestPath, err := writeManifestXML(vParts, orbitPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("creating manifest.xml: %w", err)
|
||||
}
|
||||
defer os.RemoveAll(manifestPath)
|
||||
|
||||
vi, err := createVersionInfo(vParts, manifestPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parsing versioninfo: %w", err)
|
||||
}
|
||||
|
||||
vi.Build()
|
||||
vi.Walk()
|
||||
|
||||
outPath := filepath.Join(orbitPath, "resource.syso")
|
||||
if err := vi.WriteSyso(outPath, "amd64"); err != nil {
|
||||
return fmt.Errorf("creating syso file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2,6 +2,27 @@ package packaging
|
||||
|
||||
import "text/template"
|
||||
|
||||
// Adapted from
|
||||
// https://github.com/josephspurrier/goversioninfo/blob/master/testdata/resource/goversioninfo.exe.manifest
|
||||
var ManifestXMLTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
|
||||
<assemblyIdentity
|
||||
type="win32"
|
||||
name="Fleet osquery"
|
||||
version="{{.Version}}"
|
||||
processorArchitecture="*"/>
|
||||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<security>
|
||||
<requestedPrivileges>
|
||||
<requestedExecutionLevel
|
||||
level="asInvoker"
|
||||
uiAccess="false"/>
|
||||
</requestedPrivileges>
|
||||
</security>
|
||||
</trustInfo>
|
||||
</assembly>`))
|
||||
|
||||
// Partially adapted from Launcher's wix XML in
|
||||
// https://github.com/kolide/launcher/blob/master/pkg/packagekit/internal/assets/main.wxs.
|
||||
var windowsWixTemplate = template.Must(template.New("").Option("missingkey=error").Parse(
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package packaging
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCreateVersionInfo(t *testing.T) {
|
||||
manifestPath := filepath.Join(t.TempDir(), "somefile.json")
|
||||
|
||||
t.Run("invalid version parts", func(t *testing.T) {
|
||||
parts := []string{"1", "a", "3", "c"}
|
||||
result, err := createVersionInfo(parts, manifestPath)
|
||||
require.ErrorContains(t, err, "error parsing version part")
|
||||
require.Nil(t, result)
|
||||
})
|
||||
|
||||
t.Run("creates a VersionInfo struct", func(t *testing.T) {
|
||||
parts := []string{"1", "2", "3", "0"}
|
||||
result, err := createVersionInfo(parts, manifestPath)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NotNil(t, result)
|
||||
|
||||
require.Equal(t, result.FixedFileInfo.FileVersion.Major, 1)
|
||||
require.Equal(t, result.FixedFileInfo.FileVersion.Minor, 2)
|
||||
require.Equal(t, result.FixedFileInfo.FileVersion.Patch, 3)
|
||||
require.Equal(t, result.FixedFileInfo.FileVersion.Build, 0)
|
||||
|
||||
require.Equal(t, result.FixedFileInfo.ProductVersion.Major, 1)
|
||||
require.Equal(t, result.FixedFileInfo.ProductVersion.Minor, 2)
|
||||
require.Equal(t, result.FixedFileInfo.ProductVersion.Patch, 3)
|
||||
require.Equal(t, result.FixedFileInfo.ProductVersion.Build, 0)
|
||||
|
||||
require.Equal(t, result.FixedFileInfo.FileFlagsMask, "3f")
|
||||
require.Equal(t, result.FixedFileInfo.FileFlags, "00")
|
||||
require.Equal(t, result.FixedFileInfo.FileOS, "040004")
|
||||
require.Equal(t, result.FixedFileInfo.FileType, "01")
|
||||
require.Equal(t, result.FixedFileInfo.FileSubType, "00")
|
||||
|
||||
require.Equal(t, result.StringFileInfo.Comments, "Fleet osquery")
|
||||
require.Equal(t, result.StringFileInfo.CompanyName, "Fleet Device Management (fleetdm.com)")
|
||||
require.Equal(t, result.StringFileInfo.FileDescription, "Fleet osquery installer")
|
||||
require.Equal(t, result.StringFileInfo.FileVersion, "1.2.3.0")
|
||||
require.Equal(t, result.StringFileInfo.LegalCopyright, fmt.Sprintf("%d Fleet Device Management Inc.", time.Now().Year()))
|
||||
require.Equal(t, result.StringFileInfo.ProductName, "Fleet osquery")
|
||||
require.Equal(t, result.StringFileInfo.ProductVersion, "1.2.3.0")
|
||||
require.Equal(t, result.ManifestPath, manifestPath)
|
||||
})
|
||||
}
|
||||
|
||||
func TestWriteResourceSyso(t *testing.T) {
|
||||
t.Run("removes intermediary manifest.xml file", func(t *testing.T) {
|
||||
path := t.TempDir()
|
||||
opt := Options{Version: "1.2.3"}
|
||||
|
||||
err := writeResourceSyso(opt, path)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.NoFileExists(t, filepath.Join(path, "manifest.xml"))
|
||||
})
|
||||
}
|
||||
|
||||
func TestSanitizeVersion(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Version string
|
||||
Parts []string
|
||||
ErrorsOut bool
|
||||
}{
|
||||
{Version: "4.13.0", Parts: []string{"4", "13", "0", "0"}},
|
||||
{Version: "4.13.0.1", Parts: []string{"4", "13", "0", "1"}},
|
||||
{Version: "4.13.0.1.2", Parts: []string{"4", "13", "0", "1"}},
|
||||
{Version: "4", ErrorsOut: true},
|
||||
{Version: "4.13", ErrorsOut: true},
|
||||
{Version: "bad bad", ErrorsOut: true},
|
||||
}
|
||||
|
||||
for _, tC := range testCases {
|
||||
result, err := SanitizeVersion(tC.Version)
|
||||
|
||||
if tC.ErrorsOut {
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
require.Equal(t, tC.Parts, result)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,224 @@
|
||||
package main
|
||||
|
||||
// This tool builds Orbit binaries with versioninfo information.
|
||||
// https://learn.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/constant"
|
||||
"github.com/fleetdm/fleet/v4/orbit/pkg/packaging"
|
||||
"github.com/josephspurrier/goversioninfo"
|
||||
zlog "github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Input flags
|
||||
flagVersion := flag.String("version", "0.0.1", "Version string")
|
||||
flagCreateResource := flag.Bool("resource", false, "This is a bool flag to just create the resource.syso file and not build the binary")
|
||||
flagIcon := flag.String("icon", "windows_app.ico", "Path to the icon file to embed on the binary")
|
||||
flagOutputBinary := flag.String("output", "output.exe", "Path to the output binary")
|
||||
flagCmdDir := flag.String("input", "", "Path to the directory containing the utility to build")
|
||||
|
||||
flag.Usage = func() {
|
||||
zlog.Fatal().Msgf("Usage: %s -version <version> -input <dir_path> -output <output_binary>\n", os.Args[0])
|
||||
}
|
||||
flag.Parse()
|
||||
|
||||
// check if flagCmdDir is a valid directory
|
||||
_, err := os.Stat(*flagCmdDir)
|
||||
if err != nil {
|
||||
zlog.Fatal().Err(err).Msg("invalid cmd-dir")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// now we need to create the 'resource.syso' metadata file which contains versioninfo data
|
||||
|
||||
// lets start with sanitizing the version data
|
||||
vParts, err := packaging.SanitizeVersion(*flagVersion)
|
||||
if err != nil {
|
||||
zlog.Fatal().Err(err).Msgf("invalid version: %s", *flagVersion)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// then we need to create the manifest.xml file
|
||||
manifestPath, err := writeManifestXML(vParts, *flagCmdDir)
|
||||
if err != nil {
|
||||
zlog.Fatal().Err(err).Msg("creating manifest.xml")
|
||||
os.Exit(1)
|
||||
}
|
||||
if !*flagCreateResource {
|
||||
defer os.Remove(manifestPath)
|
||||
}
|
||||
|
||||
// now we can create the VersionInfo struct
|
||||
targetIconPath := filepath.Join(*flagCmdDir, *flagIcon)
|
||||
vi, err := createVersionInfo(vParts, targetIconPath, manifestPath)
|
||||
if err != nil {
|
||||
zlog.Fatal().Err(err).Msg("parsing versioninfo")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// and finally we can write the 'resource.syso' file
|
||||
vi.Build()
|
||||
vi.Walk()
|
||||
|
||||
// resource.syso is the resource file that is going to be picked up by golang compiler
|
||||
outPath := filepath.Join(*flagCmdDir, "resource.syso")
|
||||
if err := vi.WriteSyso(outPath, "amd64"); err != nil {
|
||||
zlog.Fatal().Err(err).Msg("creating syso file")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// checking if build should be performed or not
|
||||
if *flagCreateResource {
|
||||
zlog.Info().Msgf("Successfully created resource file %s", outPath)
|
||||
} else {
|
||||
|
||||
defer os.Remove(outPath)
|
||||
// now we can build the binary
|
||||
if err := buildTargetBinary(*flagCmdDir, *flagVersion, *flagOutputBinary); err != nil {
|
||||
zlog.Fatal().Err(err).Msg("error building binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
zlog.Info().Msgf("Successfully built %s", *flagOutputBinary)
|
||||
}
|
||||
}
|
||||
|
||||
// createVersionInfo returns a VersionInfo struct pointer to be used to generate the 'resource.syso'
|
||||
// metadata file (see writeResourceSyso).
|
||||
func createVersionInfo(vParts []string, iconPath string, manifestPath string) (*goversioninfo.VersionInfo, error) {
|
||||
vIntParts := make([]int, 0, len(vParts))
|
||||
for _, p := range vParts {
|
||||
v, err := strconv.Atoi(p)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing version part %s: %w", p, err)
|
||||
}
|
||||
vIntParts = append(vIntParts, v)
|
||||
}
|
||||
version := strings.Join(vParts, ".")
|
||||
copyright := fmt.Sprintf("%d Fleet Device Management Inc.", time.Now().Year())
|
||||
|
||||
// Taken from https://github.com/josephspurrier/goversioninfo/blob/master/testdata/resource/versioninfo.json
|
||||
langID, err := strconv.ParseUint("0409", 16, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid LangID")
|
||||
}
|
||||
// Taken from https://github.com/josephspurrier/goversioninfo/blob/master/testdata/resource/versioninfo.json
|
||||
charsetID, err := strconv.ParseUint("04B0", 16, 16)
|
||||
if err != nil {
|
||||
return nil, errors.New("invalid charsetID")
|
||||
}
|
||||
|
||||
result := goversioninfo.VersionInfo{
|
||||
FixedFileInfo: goversioninfo.FixedFileInfo{
|
||||
FileVersion: goversioninfo.FileVersion{
|
||||
Major: vIntParts[0],
|
||||
Minor: vIntParts[1],
|
||||
Patch: vIntParts[2],
|
||||
Build: vIntParts[3],
|
||||
},
|
||||
ProductVersion: goversioninfo.FileVersion{
|
||||
Major: vIntParts[0],
|
||||
Minor: vIntParts[1],
|
||||
Patch: vIntParts[2],
|
||||
Build: vIntParts[3],
|
||||
},
|
||||
FileFlagsMask: "3f",
|
||||
FileFlags: "00",
|
||||
FileOS: "040004",
|
||||
FileType: "01",
|
||||
FileSubType: "00",
|
||||
},
|
||||
StringFileInfo: goversioninfo.StringFileInfo{
|
||||
Comments: "Fleet osquery",
|
||||
CompanyName: "Fleet Device Management (fleetdm.com)",
|
||||
FileDescription: "Orbit osquery runtime and autoupdater",
|
||||
FileVersion: version,
|
||||
InternalName: "",
|
||||
LegalCopyright: copyright,
|
||||
LegalTrademarks: "",
|
||||
OriginalFilename: "",
|
||||
PrivateBuild: "",
|
||||
ProductName: "Fleet osquery",
|
||||
ProductVersion: version,
|
||||
SpecialBuild: "",
|
||||
},
|
||||
VarFileInfo: goversioninfo.VarFileInfo{
|
||||
Translation: goversioninfo.Translation{
|
||||
LangID: goversioninfo.LangID(langID),
|
||||
CharsetID: goversioninfo.CharsetID(charsetID),
|
||||
},
|
||||
},
|
||||
IconPath: iconPath,
|
||||
ManifestPath: manifestPath,
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// writeManifestXML creates the manifest.xml file used when generating the 'resource.syso' metadata
|
||||
// (see writeResourceSyso). Returns the path of the newly created file.
|
||||
func writeManifestXML(vParts []string, orbitPath string) (string, error) {
|
||||
filePath := filepath.Join(orbitPath, "manifest.xml")
|
||||
|
||||
tmplOpts := struct {
|
||||
Version string
|
||||
}{
|
||||
Version: strings.Join(vParts, "."),
|
||||
}
|
||||
|
||||
var contents bytes.Buffer
|
||||
|
||||
if err := packaging.ManifestXMLTemplate.Execute(&contents, tmplOpts); err != nil {
|
||||
return "", fmt.Errorf("parsing manifest.xml template: %w", err)
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(filePath, contents.Bytes(), constant.DefaultFileMode); err != nil {
|
||||
return "", fmt.Errorf("writing manifest.xml file: %w", err)
|
||||
}
|
||||
|
||||
return filePath, nil
|
||||
}
|
||||
|
||||
// Build the target binary for Windows
|
||||
func buildTargetBinary(cmdDir string, version string, binaryPath string) error {
|
||||
var buildExec *exec.Cmd
|
||||
|
||||
// convert relative to full output path
|
||||
outputBinary, err := filepath.Abs(binaryPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting binary path to absolute: %w", err)
|
||||
}
|
||||
|
||||
// check if cmdDir contains desktop
|
||||
// if it does, add -ldflags "-H=windowsgui" to exec.Command
|
||||
if strings.Contains(cmdDir, "desktop") {
|
||||
linkFlags := fmt.Sprintf("-H=windowsgui -X=main.version=%s", version)
|
||||
buildExec = exec.Command("go", "build", "-ldflags", linkFlags, "-o", outputBinary)
|
||||
} else {
|
||||
linkFlags := fmt.Sprintf("-X=github.com/fleetdm/fleet/v4/orbit/pkg/build.Version=%s", version)
|
||||
buildExec = exec.Command("go", "build", "-ldflags", linkFlags, "-o", outputBinary)
|
||||
}
|
||||
buildExec.Env = append(os.Environ(), "GOOS=windows", "GOARCH=amd64")
|
||||
buildExec.Stderr = os.Stderr
|
||||
buildExec.Stdout = os.Stdout
|
||||
buildExec.Dir = cmdDir
|
||||
|
||||
if err := buildExec.Run(); err != nil {
|
||||
return fmt.Errorf("compile orbit: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user