Skip to content

Commit a2f6665

Browse files
authored
Merge pull request #24 from tbrittain/20-version-information
Version information in app
2 parents cf528e7 + 0a5ee9b commit a2f6665

9 files changed

Lines changed: 163 additions & 32 deletions

File tree

.github/workflows/release.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,12 @@ jobs:
7272
if: runner.os == 'Linux'
7373
run: sudo apt-get update && sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
7474

75+
- name: Generate version
76+
id: version
77+
run: echo "tag=v$(date -u +%Y.%m.%d).${{ github.run_number }}" >> "$GITHUB_OUTPUT"
78+
7579
- name: Build
76-
run: wails build -platform ${{ matrix.platform }} ${{ matrix.build-flags }}
80+
run: wails build -platform ${{ matrix.platform }} ${{ matrix.build-flags }} -ldflags "-X main.Version=${{ steps.version.outputs.tag }}"
7781

7882
- name: Zip artifact (Windows)
7983
if: runner.os == 'Windows'

CLAUDE.md

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ Git Analytics is a desktop application that provides insights and visualizations
99
- **Framework:** Wails v2 (Go backend + Vue 3/TypeScript frontend)
1010
- **Backend:** Go 1.25 (`main.go`, `app.go`)
1111
- **Frontend:** Vue 3, TypeScript, Vite (`frontend/src/`)
12-
- **Build:** Windows + macOS support via Wails
1312

1413
Primary dependencies for core functionality:
1514
- `go-git` for Git repository analysis (`github.com/go-git/go-git/v6`)
16-
- `modernc.org/sqlite` for data layer (pure Go, no CGO)
15+
- `modernc.org/sqlite` for data layer
1716
- `wailsjs` for frontend-backend communication (auto-generated by Wails)
1817

19-
Not focusing much on the UI for now until we have a solid backend foundation
20-
2118
## Development
2219

2320
```sh
@@ -45,15 +42,5 @@ build/ # Platform-specific build assets
4542

4643
## Design Goals
4744

48-
1. We will proceed with core functionality (nearly all backend) before implementing the UI.
49-
2. Code should be simple and designed for testability. Add tests for critical logic, especially around Git repository analysis and data handling. Use Go's built-in testing framework and aim for good coverage on core functions.
50-
3. Modular design - core logic should be standalone and not often to change if we want to swap out anything at the "boundary" (e.g. Git library, database, frontend framework). To this point, use interfaces and dependency injection where appropriate to decouple components (manual DI, no frameworks). For example, we can define a `GitRepository` interface that abstracts away the underlying Git library, allowing us to swap out `go-git` for another implementation in the future without affecting the rest of the codebase.
51-
52-
## General features of the app
53-
54-
- A read-only interface of a given Git repository. The Git repository should already exist on disk, and the user should be able to select it via a file dialog. Upon load, there should be a background indexing processes (details TBD) that will create a new SQLite database file in the same directory as the Git repository (automatically added to git/info/exclude so that it is never committed). This database will be used to power all analytics and visualizations in the app, and will be updated incrementally as new commits are added to the repository.
55-
- The primary features will be related to the following:
56-
- Commit graph visualization (total and by contributor)
57-
- Code change heatmaps (e.g. by time of day, day of week, etc.)
58-
- Contributor activity timelines
59-
- File change history and statistics (including churn, most changed files, etc.) in a hierarchical format (to understand changes at the directory level as well as file level)
45+
1. Code should be simple and designed for testability. Add tests for critical logic, especially around Git repository analysis and data handling. Use Go's built-in testing framework and aim for good coverage on core functions.
46+
2. Modular design - core logic should be standalone and not often to change if we want to swap out anything at the "boundary" (e.g. Git library, database, frontend framework).

README.md

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
# README
1+
# Git Analytics
22

33
## About
44

5-
This is the official Wails Vue-TS template.
6-
7-
You can configure the project by editing `wails.json`. More information about the project settings can be found
8-
here: https://wails.io/docs/reference/project-config
5+
Git Analytics is a desktop application that provides insights and visualizations for Git repositories.
96

107
## Prerequisites
118
- Go 1.25
@@ -18,7 +15,3 @@ To run in live development mode, run `wails dev` in the project directory. This
1815
server that will provide very fast hot reload of your frontend changes. If you want to develop in a browser
1916
and have access to your Go methods, there is also a dev server that runs on http://localhost:34115. Connect
2017
to this in your browser, and you can call your Go code from devtools.
21-
22-
## Building
23-
24-
To build a redistributable, production mode package, use `wails build`.

app.go

Lines changed: 61 additions & 2 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"
@@ -27,11 +29,68 @@ type App struct {
2729
store store.Store
2830
db *sql.DB
2931
configDir string
32+
version string
3033
}
3134

3235
// NewApp creates a new App application struct
33-
func NewApp() *App {
34-
return &App{}
36+
func NewApp(version string) *App {
37+
return &App{version: version}
38+
}
39+
40+
// Version returns the application version string.
41+
func (a *App) Version() string {
42+
return a.version
43+
}
44+
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+
}
3594
}
3695

3796
// startup is called when the app starts. The context is saved

frontend/src/App.vue

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<script lang="ts" setup>
2-
import { provide, ref } from 'vue'
3-
import { OpenRepository, SelectDirectory } from '../wailsjs/go/main/App'
2+
import { onMounted, provide, ref } from 'vue'
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
@@ -9,6 +9,22 @@ provide('repoPath', repoPath)
99
const loading = ref(false)
1010
const error = ref('')
1111
const repoReady = ref(false)
12+
const appVersion = ref('')
13+
const updateURL = ref('')
14+
const updateTag = ref('')
15+
16+
onMounted(async () => {
17+
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+
}
27+
})
1228
1329
async function onSelectRepo() {
1430
const path = await SelectDirectory()
@@ -76,6 +92,14 @@ async function onOpenRecent(path: string) {
7692
</div>
7793
<router-view v-else :key="repoPath" />
7894
</main>
95+
<footer v-if="appVersion">
96+
<a href="https://github.com/tbrittain/git-analytics" target="_blank" rel="noopener">Git Analytics</a>
97+
<span class="separator">·</span>
98+
<span>{{ appVersion }}</span>
99+
<a v-if="updateURL" :href="updateURL" target="_blank" rel="noopener" class="update-link">
100+
Update available: {{ updateTag }}
101+
</a>
102+
</footer>
79103
</div>
80104
</template>
81105

@@ -140,6 +164,40 @@ main {
140164
min-height: 0;
141165
}
142166
167+
footer {
168+
display: flex;
169+
align-items: center;
170+
gap: 8px;
171+
padding: 8px 20px;
172+
border-top: 1px solid #30363d;
173+
color: #8b949e;
174+
font-size: 12px;
175+
flex-shrink: 0;
176+
}
177+
178+
footer a {
179+
color: #8b949e;
180+
text-decoration: none;
181+
}
182+
183+
footer a:hover {
184+
color: #c9d1d9;
185+
text-decoration: underline;
186+
}
187+
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+
143201
.status {
144202
display: flex;
145203
align-items: center;

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

Lines changed: 5 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

@@ -29,3 +31,5 @@ export function RepoInfo():Promise<main.RepoInfo>;
2931
export function SelectDirectory():Promise<string>;
3032

3133
export function TemporalHotspots(arg1:string,arg2:string,arg3:number,arg4:Array<string>):Promise<Array<query.TemporalHotspot>>;
34+
35+
export function Version():Promise<string>;

frontend/wailsjs/go/main/App.js

Lines changed: 8 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
}
@@ -53,3 +57,7 @@ export function SelectDirectory() {
5357
export function TemporalHotspots(arg1, arg2, arg3, arg4) {
5458
return window['go']['main']['App']['TemporalHotspots'](arg1, arg2, arg3, arg4);
5559
}
60+
61+
export function Version() {
62+
return window['go']['main']['App']['Version']();
63+
}

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

main.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import (
1111
//go:embed all:frontend/dist
1212
var assets embed.FS
1313

14+
var Version = "dev"
15+
1416
func main() {
1517
// Create an instance of the app structure
16-
app := NewApp()
18+
app := NewApp(Version)
1719

1820
// Create application with options
1921
err := wails.Run(&options.App{

0 commit comments

Comments
 (0)