Skip to content

Commit 09de3a2

Browse files
committed
Add check for newer release
This is a crude implementation to test release integration.
1 parent 5d265c5 commit 09de3a2

File tree

7 files changed

+203
-8
lines changed

7 files changed

+203
-8
lines changed

resources/installer.nsi

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
!define APP_NAME "ICARUS Terminal"
88
!define COMP_NAME "ICARUS"
9-
!define VERSION "00.00.00.00"
9+
!define VERSION "0.1.1.0"
1010
!define COPYRIGHT "ICARUS"
1111
!define DESCRIPTION "Application"
1212
!define INSTALLER_NAME "..\dist\ICARUS Setup.exe"

scripts/lib/build-options.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
const path = require('path')
22

3-
const ROOT_DIR = path.join(__dirname, '..', '..')
3+
const PRODUCT_VERSION = '0.1.1.0'
4+
const APP_FILE_VERSION = PRODUCT_VERSION
5+
const SERVICE_FILE_VERSION = PRODUCT_VERSION
46

57
// Development builds are faster, larger and can contain debug routines
68
const DEVELOPMENT_BUILD = process.env.DEVELOPMENT || false
79
const DEBUG_CONSOLE = DEVELOPMENT_BUILD
810

11+
const ROOT_DIR = path.join(__dirname, '..', '..')
912
const BUILD_DIR = path.join(ROOT_DIR, 'build') // For intermediate build steps
1013
const BIN_DIR = path.join(BUILD_DIR, 'bin') // For final binary build
1114
const DIST_DIR = path.join(ROOT_DIR, 'dist') // For distributable build
@@ -16,8 +19,6 @@ const ASSETS_BUILD_DIR = path.join(BUILD_DIR, 'assets')
1619
const INSTALLER_NSI = path.join(RESOURCES_DIR, 'installer.nsi') // Installer config
1720
const INSTALLER_EXE = path.join(DIST_DIR, 'ICARUS Setup.exe') // Should match INSTALLER_NAME in .nsi
1821

19-
const PRODUCT_VERSION = '0.0.0.1'
20-
2122
const APP_BINARY_NAME = 'ICARUS Terminal.exe'
2223
const APP_UNOPTIMIZED_BUILD = path.join(BUILD_DIR, `~UNOPT_${safeBinaryName(APP_BINARY_NAME)}`)
2324
const APP_OPTIMIZED_BUILD = path.join(BUILD_DIR, `~OPT_${safeBinaryName(APP_BINARY_NAME)}`)
@@ -28,7 +29,7 @@ const APP_VERSION_INFO = {
2829
CompanyName: 'ICARUS',
2930
ProductName: 'ICARUS Terminal',
3031
FileDescription: 'ICARUS Terminal',
31-
FileVersion: '0.0.0.1',
32+
FileVersion: APP_FILE_VERSION,
3233
ProductVersion: PRODUCT_VERSION,
3334
OriginalFilename: 'ICARUS Terminal.exe',
3435
InternalName: 'ICARUS Terminal',
@@ -45,7 +46,7 @@ const SERVICE_VERSION_INFO = {
4546
CompanyName: 'ICARUS',
4647
ProductName: 'ICARUS Terminal Service',
4748
FileDescription: 'ICARUS Terminal Service',
48-
FileVersion: '0.0.0.1',
49+
FileVersion: SERVICE_FILE_VERSION,
4950
ProductVersion: PRODUCT_VERSION,
5051
OriginalFilename: 'ICARUS Service.exe',
5152
InternalName: 'ICARUS Service',

src/app/go.mod

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
module iaincollins.com/m/v2
1+
module iaincollins.com/icarus-terminal/v2
22

33
go 1.17
44

55
require github.com/webview/webview v0.0.0-20210330151455-f540d88dde4e
66

77
require (
88
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf // indirect
9+
github.com/gonutz/w32/v2 v2.2.2 // indirect
910
github.com/jchv/go-webview2 v0.0.0-20211023023319-977d8719321f // indirect
1011
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 // indirect
12+
github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e // indirect
1113
github.com/nvsoft/win v0.0.0-20160111051136-23d143e32c41 // indirect
1214
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 // indirect
1315
github.com/sqweek/dialog v0.0.0-20211002065838-9a201b55ab91 // indirect

src/app/go.sum

+4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I=
22
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
3+
github.com/gonutz/w32/v2 v2.2.2 h1:y6Y337TpuCXjYdFTq5p5NmcujEdAQiTB43kisovMk+0=
4+
github.com/gonutz/w32/v2 v2.2.2/go.mod h1:MgtHx0AScDVNKyB+kjyPder4xIi3XAcHS6LDDU2DmdE=
35
github.com/jchv/go-webview2 v0.0.0-20211023023319-977d8719321f h1:wF7bbDOcRcRlamAwWMNagyFVQubISAlisj6Ix8O8Hn0=
46
github.com/jchv/go-webview2 v0.0.0-20211023023319-977d8719321f/go.mod h1:7Q5nFip7HvzGJDYVfa22s/pb9T2X+XhEjLhylNf5dV8=
57
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5 h1:pdFFlHXY9tZXmJz+tRSm1DzYEH4ebha7cffmm607bMU=
68
github.com/jchv/go-winloader v0.0.0-20200815041850-dec1ee9a7fd5/go.mod h1:alcuEEnZsY1WQsagKhZDsoPCRoOijYqhZvPwLG0kzVs=
9+
github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e h1:ZZCvgaRDZg1gC9/1xrsgaJzQUCQgniKtw0xjWywWAOE=
10+
github.com/jmoiron/jsonq v0.0.0-20150511023944-e874b168d07e/go.mod h1:+rHyWac2R9oAZwFe1wGY2HBzFJJy++RHBg1cU23NkD8=
711
github.com/nvsoft/win v0.0.0-20160111051136-23d143e32c41 h1:s0qXnW0MxcRPYZpqbrITRo3tAbAdlQBPUCKf/akNMKg=
812
github.com/nvsoft/win v0.0.0-20160111051136-23d143e32c41/go.mod h1:bI2vvx1dagFt7tydvy947C0q6ET6k5MfIvWmmpLelpw=
913
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 h1:JhzVVoYvbOACxoUmOs6V/G4D5nPVUW73rKvXxP4XUJc=

src/app/main.go

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ var processGroup ProcessGroup
4848
func main() {
4949
startTime := time.Now()
5050

51+
CheckForUpdate()
52+
5153
_processGroup, err := NewProcessGroup()
5254
if err != nil {
5355
panic(err)

src/app/updater.go

+186
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
package main
2+
3+
import (
4+
"strings"
5+
"encoding/json"
6+
"github.com/jmoiron/jsonq"
7+
"github.com/sqweek/dialog"
8+
"github.com/gonutz/w32/v2"
9+
"io/ioutil"
10+
"net/http"
11+
"time"
12+
"regexp"
13+
"os/exec"
14+
"errors"
15+
"io"
16+
"os"
17+
)
18+
19+
const LATEST_RELEASE_URL = "https://api.github.com/repos/iaincollins/icarus/releases/latest"
20+
21+
type Release struct {
22+
productVersion string
23+
downloadUrl string
24+
}
25+
26+
func CheckForUpdate() {
27+
currentProductVersion := GetCurrentAppVersion()
28+
release, releaseErr := GetLatestRelease(LATEST_RELEASE_URL)
29+
if releaseErr != nil {
30+
return
31+
}
32+
33+
// If we are already running the latest release, do nothing
34+
if (currentProductVersion == release.productVersion) {
35+
return
36+
}
37+
38+
ok := dialog.Message("%s", "A new version of ICARUS Terminal is available.\n\nWould you like to download the update?").Title("New version available").YesNo()
39+
if (ok) {
40+
downloadUrl := release.downloadUrl // In future may redirect to webpage instead
41+
exec.Command("rundll32", "url.dll,FileProtocolHandler", downloadUrl).Start()
42+
43+
// This is disabled as we can't actually run the current installer this way
44+
// because it requires escalated privilages. This could be addressed by
45+
// using a different type of installer.
46+
/*
47+
downloadedFile, downloadErr := DownloadRelease(release)
48+
if downloadErr != nil {
49+
fmt.Println("Error downloading update", downloadErr.Error())
50+
}
51+
52+
installerCmdInstance := exec.Command(downloadedFile)
53+
installerCmdErr := installerCmdInstance.Start()
54+
if installerCmdErr != nil {
55+
fmt.Println("Error installing update", installerCmdErr.Error())
56+
}
57+
*/
58+
}
59+
60+
return
61+
}
62+
63+
func GetCurrentAppVersion() string {
64+
const path = "ICARUS Terminal.exe"
65+
66+
size := w32.GetFileVersionInfoSize(path)
67+
if size <= 0 {
68+
panic("GetFileVersionInfoSize failed")
69+
}
70+
71+
info := make([]byte, size)
72+
ok := w32.GetFileVersionInfo(path, info)
73+
if !ok {
74+
panic("GetFileVersionInfo failed")
75+
}
76+
77+
/*
78+
fixed, ok := w32.VerQueryValueRoot(info)
79+
if !ok {
80+
panic("VerQueryValueRoot failed")
81+
}
82+
version := fixed.FileVersion()
83+
fileVersion := fmt.Sprintf(
84+
"%d.%d.%d.%d",
85+
version&0xFFFF000000000000>>48,
86+
version&0x0000FFFF00000000>>32,
87+
version&0x00000000FFFF0000>>16,
88+
version&0x000000000000FFFF>>0,
89+
)
90+
*/
91+
92+
translations, ok := w32.VerQueryValueTranslations(info)
93+
if !ok {
94+
panic("VerQueryValueTranslations failed")
95+
}
96+
if len(translations) == 0 {
97+
panic("no translation found")
98+
}
99+
t := translations[0]
100+
101+
productVersion, ok := w32.VerQueryValueString(info, t, w32.ProductVersion)
102+
if !ok {
103+
panic("cannot get product version")
104+
}
105+
106+
// Convert from version with build number (0.0.0.0) to semver version (0.0.0)
107+
productVersion = regexp.MustCompile(`(\.[^\.]+)$`).ReplaceAllString(productVersion, ``)
108+
109+
return productVersion
110+
}
111+
112+
func GetLatestRelease(releasesUrl string) (Release, error) {
113+
release := Release{}
114+
115+
httpClient := http.Client{Timeout: time.Second * 5}
116+
117+
req, reqErr := http.NewRequest(http.MethodGet, releasesUrl, nil)
118+
if reqErr != nil {
119+
return release, reqErr
120+
}
121+
122+
res, getErr := httpClient.Do(req)
123+
if getErr != nil {
124+
return release, getErr
125+
}
126+
127+
if res.Body != nil {
128+
defer res.Body.Close()
129+
}
130+
131+
body, readErr := ioutil.ReadAll(res.Body)
132+
if readErr != nil {
133+
return release, readErr
134+
}
135+
136+
// Hackery to convert the response into JSON that jsonq can parse
137+
jsonObjectAsString := string(body)
138+
// jsonObjectAsString = regexp.MustCompile(`^\[`).ReplaceAllString(jsonObjectAsString, `{"releases":[`)
139+
// jsonObjectAsString = regexp.MustCompile(`\]$`).ReplaceAllString(jsonObjectAsString, `]}`)
140+
141+
// Use jsonq to access JSON
142+
data := map[string]interface{}{}
143+
dec := json.NewDecoder(strings.NewReader(jsonObjectAsString))
144+
dec.Decode(&data)
145+
jq := jsonq.NewQuery(data)
146+
147+
// Get properties from from JSON
148+
// tag, _ := jq.String("releases", "0", "tag_name")
149+
// productVersion := regexp.MustCompile(`^v`).ReplaceAllString(tag, ``)
150+
// downloadUrl, _ := jq.String("releases", "0", "assets", "0", "browser_download_url")
151+
tag, _ := jq.String("tag_name")
152+
productVersion := regexp.MustCompile(`^v`).ReplaceAllString(tag, ``) // Converts tag (v0.0.0) to semver version (0.0.0) for easier comparion
153+
downloadUrl, _ := jq.String("assets", "0", "browser_download_url")
154+
155+
if (downloadUrl == "") {
156+
return release, errors.New("Could not get download URL")
157+
}
158+
159+
release.productVersion = productVersion
160+
release.downloadUrl = downloadUrl
161+
162+
return release, nil
163+
}
164+
165+
func DownloadRelease(release Release) (string, error) {
166+
pathToDownloadedFile := "ICARUS Update.exe"
167+
168+
// Get file to download
169+
resp, err := http.Get(release.downloadUrl)
170+
if err != nil {
171+
return "", err
172+
}
173+
defer resp.Body.Close()
174+
175+
// Create file
176+
out, err := os.Create(pathToDownloadedFile)
177+
if err != nil {
178+
return "", err
179+
}
180+
defer out.Close()
181+
182+
// Write to file
183+
_, err = io.Copy(out, resp.Body)
184+
185+
return pathToDownloadedFile, nil
186+
}

src/web/public/launcher.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ <h1>ICARUS</h1>
5858
<button onClick="window.app_quit()">Quit Application</button>
5959
</p>
6060
<p>
61-
<small>Preview Build 0.0.0.1</small>
61+
<small>Preview Build 0.1.1.0</small>
6262
</p>
6363
</div>
6464
</body>

0 commit comments

Comments
 (0)