Skip to content

Commit 0a5ee9b

Browse files
committed
feat: display if new version available
1 parent 67ea0ef commit 0a5ee9b

5 files changed

Lines changed: 106 additions & 3 deletions

File tree

app.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package main
33
import (
44
"context"
55
"database/sql"
6+
"encoding/json"
67
"fmt"
8+
"net/http"
79
"os"
810
"path/filepath"
911
"strings"
@@ -40,6 +42,57 @@ func (a *App) Version() string {
4042
return a.version
4143
}
4244

45+
// UpdateInfo holds information about an available update.
46+
type UpdateInfo struct {
47+
Available bool `json:"available"`
48+
Tag string `json:"tag"`
49+
URL string `json:"url"`
50+
}
51+
52+
// CheckForUpdate queries GitHub for the latest release and returns update
53+
// info if a newer version is available. Returns Available=false on any error
54+
// or if already up to date.
55+
func (a *App) CheckForUpdate() UpdateInfo {
56+
if a.version == "dev" {
57+
return UpdateInfo{}
58+
}
59+
60+
client := &http.Client{Timeout: 5 * time.Second}
61+
req, err := http.NewRequest("GET", "https://api.github.com/repos/tbrittain/git-analytics/releases/latest", nil)
62+
if err != nil {
63+
return UpdateInfo{}
64+
}
65+
req.Header.Set("User-Agent", "git-analytics/"+a.version)
66+
67+
resp, err := client.Do(req)
68+
if err != nil {
69+
return UpdateInfo{}
70+
}
71+
defer resp.Body.Close()
72+
73+
if resp.StatusCode != http.StatusOK {
74+
return UpdateInfo{}
75+
}
76+
77+
var release struct {
78+
TagName string `json:"tag_name"`
79+
HTMLURL string `json:"html_url"`
80+
}
81+
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
82+
return UpdateInfo{}
83+
}
84+
85+
if release.TagName == "" || release.TagName == a.version {
86+
return UpdateInfo{}
87+
}
88+
89+
return UpdateInfo{
90+
Available: true,
91+
Tag: release.TagName,
92+
URL: release.HTMLURL,
93+
}
94+
}
95+
4396
// startup is called when the app starts. The context is saved
4497
// so we can call the runtime methods
4598
func (a *App) startup(ctx context.Context) {

frontend/src/App.vue

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
22
import { onMounted, provide, ref } from 'vue'
3-
import { OpenRepository, SelectDirectory, Version } from '../wailsjs/go/main/App'
3+
import { CheckForUpdate, OpenRepository, SelectDirectory, Version } from '../wailsjs/go/main/App'
44
import RepoSelector from './components/RepoSelector.vue'
55
import RecentReposList from './components/RecentReposList.vue'
66
@@ -10,9 +10,20 @@ const loading = ref(false)
1010
const error = ref('')
1111
const repoReady = ref(false)
1212
const appVersion = ref('')
13+
const updateURL = ref('')
14+
const updateTag = ref('')
1315
1416
onMounted(async () => {
1517
appVersion.value = await Version()
18+
try {
19+
const info = await CheckForUpdate()
20+
if (info.available) {
21+
updateTag.value = info.tag
22+
updateURL.value = info.url
23+
}
24+
} catch {
25+
// Silently ignore update check failures
26+
}
1627
})
1728
1829
async function onSelectRepo() {
@@ -83,7 +94,11 @@ async function onOpenRecent(path: string) {
8394
</main>
8495
<footer v-if="appVersion">
8596
<a href="https://github.com/tbrittain/git-analytics" target="_blank" rel="noopener">Git Analytics</a>
97+
<span class="separator">·</span>
8698
<span>{{ appVersion }}</span>
99+
<a v-if="updateURL" :href="updateURL" target="_blank" rel="noopener" class="update-link">
100+
Update available: {{ updateTag }}
101+
</a>
87102
</footer>
88103
</div>
89104
</template>
@@ -151,8 +166,8 @@ main {
151166
152167
footer {
153168
display: flex;
154-
justify-content: space-between;
155169
align-items: center;
170+
gap: 8px;
156171
padding: 8px 20px;
157172
border-top: 1px solid #30363d;
158173
color: #8b949e;
@@ -170,6 +185,19 @@ footer a:hover {
170185
text-decoration: underline;
171186
}
172187
188+
.separator {
189+
color: #30363d;
190+
}
191+
192+
.update-link {
193+
margin-left: auto;
194+
color: #58a6ff;
195+
}
196+
197+
.update-link:hover {
198+
color: #79c0ff;
199+
}
200+
173201
.status {
174202
display: flex;
175203
align-items: center;

frontend/wailsjs/go/main/App.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
22
// This file is automatically generated. DO NOT EDIT
3+
import {main} from '../models';
34
import {query} from '../models';
45
import {config} from '../models';
5-
import {main} from '../models';
6+
7+
export function CheckForUpdate():Promise<main.UpdateInfo>;
68

79
export function CoChanges(arg1:string,arg2:string,arg3:number,arg4:number,arg5:Array<string>):Promise<Array<query.CoChangePair>>;
810

frontend/wailsjs/go/main/App.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
33
// This file is automatically generated. DO NOT EDIT
44

5+
export function CheckForUpdate() {
6+
return window['go']['main']['App']['CheckForUpdate']();
7+
}
8+
59
export function CoChanges(arg1, arg2, arg3, arg4, arg5) {
610
return window['go']['main']['App']['CoChanges'](arg1, arg2, arg3, arg4, arg5);
711
}

frontend/wailsjs/go/models.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ export namespace main {
6464
this.last_commit_age = source["last_commit_age"];
6565
}
6666
}
67+
export class UpdateInfo {
68+
available: boolean;
69+
tag: string;
70+
url: string;
71+
72+
static createFrom(source: any = {}) {
73+
return new UpdateInfo(source);
74+
}
75+
76+
constructor(source: any = {}) {
77+
if ('string' === typeof source) source = JSON.parse(source);
78+
this.available = source["available"];
79+
this.tag = source["tag"];
80+
this.url = source["url"];
81+
}
82+
}
6783

6884
}
6985

0 commit comments

Comments
 (0)