Skip to content

Commit f5f482f

Browse files
author
hiqqs
committed
Add GitHub release publishing
1 parent a6a9ca4 commit f5f482f

6 files changed

Lines changed: 333 additions & 18 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@ jobs:
2828
run: npm run build
2929

3030
- name: Run Themis tests
31-
run: npx themis test
31+
run: npm run test

.github/workflows/release.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
12+
jobs:
13+
publish:
14+
runs-on: windows-latest
15+
16+
steps:
17+
- name: Check out repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: 22
24+
cache: npm
25+
26+
- name: Install dependencies
27+
run: npm ci
28+
29+
- name: Lint
30+
run: npm run lint
31+
32+
- name: Build
33+
run: npm run build
34+
35+
- name: Run Themis tests
36+
run: npm run test
37+
38+
- name: Build and publish Windows installer
39+
run: npm run release:ci
40+
env:
41+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ The application lets teams:
1515

1616
## Version
1717

18-
Current app version: `0.2.2`
18+
Current app version: `0.2.3`
19+
20+
For releases, keep the `package.json` version and Git tag aligned. Example: `0.2.3` in `package.json` should be released as `v0.2.3`.
1921

2022
## Development
2123

@@ -60,11 +62,24 @@ That command runs the app build first, then packages the Electron app with `elec
6062

6163
After it finishes, look in the `release` folder for:
6264

63-
- `Code Release Tracker Setup 0.2.2.exe`: the Windows installer
65+
- `Code Release Tracker Setup 0.2.3.exe`: the Windows installer
6466
- `win-unpacked`: the unpacked app directory
6567

6668
To install locally, run the installer `.exe` and follow the setup wizard. You can also use the unpacked folder directly if you want to test the app without installing it.
6769

70+
## App Updates
71+
72+
Published Windows installers are stored in the repository's GitHub Releases. The app checks that release feed on startup when running from a packaged Windows build, and prompts you to restart after a downloaded update is ready.
73+
74+
To publish a new Windows release from CI or a tag build, use:
75+
76+
```powershell
77+
npm run release:ci
78+
```
79+
80+
If you just want to build the installer locally without publishing it, use `npm run dist:win`.
81+
Do not run `npm run release:ci` locally; it is meant for GitHub Actions publishing only.
82+
6883
## Project Structure
6984

7085
- `src/App.tsx`: main application UI and state management

electron.cjs

Lines changed: 172 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
const { app, BrowserWindow, ipcMain } = require("electron");
1+
const { app, BrowserWindow, dialog, ipcMain } = require("electron");
2+
const { autoUpdater } = require("electron-updater");
23
const path = require("path");
4+
const { pathToFileURL } = require("url");
35

46
const isDev = !app.isPackaged;
57
const WINDOW_TITLE = "Code Release Tracker";
8+
const SPLASH_IMAGE = path.join(__dirname, "src", "assets", "codeReleaseTrackerBg.png");
69
const WINDOW_ICON = path.join(
710
__dirname,
811
"src",
@@ -12,6 +15,153 @@ const WINDOW_ICON = path.join(
1215

1316
app.setName(WINDOW_TITLE);
1417

18+
function setupAutoUpdater() {
19+
if (process.platform !== "win32" || !app.isPackaged) {
20+
return;
21+
}
22+
23+
autoUpdater.autoDownload = true;
24+
autoUpdater.autoInstallOnAppQuit = true;
25+
26+
autoUpdater.on("update-available", (info) => {
27+
console.log(`Update available: ${info.version}`);
28+
});
29+
30+
autoUpdater.on("update-not-available", (info) => {
31+
console.log(`No update available: ${info.version}`);
32+
});
33+
34+
autoUpdater.on("error", (error) => {
35+
console.error("Auto-update error:", error);
36+
});
37+
38+
autoUpdater.on("update-downloaded", async (info) => {
39+
const result = await dialog.showMessageBox({
40+
type: "info",
41+
buttons: ["Restart now", "Later"],
42+
defaultId: 0,
43+
cancelId: 1,
44+
title: WINDOW_TITLE,
45+
message: "A new version of Code Release Tracker is ready to install.",
46+
detail: `Version ${info.version} has been downloaded. Restart now to finish installing the update.`,
47+
});
48+
49+
if (result.response === 0) {
50+
autoUpdater.quitAndInstall();
51+
}
52+
});
53+
54+
autoUpdater.checkForUpdates().catch((error) => {
55+
console.error("Failed to check for updates:", error);
56+
});
57+
}
58+
59+
function createSplashWindow() {
60+
const splashImageUrl = pathToFileURL(SPLASH_IMAGE).href;
61+
const splash = new BrowserWindow({
62+
width: 760,
63+
height: 440,
64+
frame: false,
65+
resizable: false,
66+
movable: true,
67+
maximizable: false,
68+
minimizable: false,
69+
fullscreenable: false,
70+
show: true,
71+
skipTaskbar: true,
72+
backgroundColor: "#040611",
73+
autoHideMenuBar: true,
74+
webPreferences: {
75+
contextIsolation: true,
76+
},
77+
});
78+
79+
splash.setMenuBarVisibility(false);
80+
splash.loadURL(
81+
`data:text/html;charset=UTF-8,${encodeURIComponent(`
82+
<!doctype html>
83+
<html lang="en">
84+
<head>
85+
<meta charset="UTF-8" />
86+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
87+
<title>${WINDOW_TITLE}</title>
88+
<style>
89+
:root {
90+
color-scheme: dark;
91+
font-family: "Segoe UI", system-ui, sans-serif;
92+
}
93+
94+
html, body {
95+
width: 100%;
96+
height: 100%;
97+
margin: 0;
98+
}
99+
100+
body {
101+
display: grid;
102+
place-items: center;
103+
background:
104+
linear-gradient(180deg, rgba(4, 6, 17, 0.18), rgba(4, 6, 17, 0.82)),
105+
url("${splashImageUrl}") center/cover no-repeat;
106+
overflow: hidden;
107+
}
108+
109+
.panel {
110+
display: grid;
111+
gap: 12px;
112+
justify-items: center;
113+
padding: 18px 22px;
114+
border: 1px solid rgba(255, 255, 255, 0.14);
115+
border-radius: 18px;
116+
background: rgba(5, 6, 15, 0.58);
117+
box-shadow: 0 20px 80px rgba(0, 0, 0, 0.45);
118+
backdrop-filter: blur(10px);
119+
}
120+
121+
.title {
122+
margin: 0;
123+
font-size: 18px;
124+
font-weight: 700;
125+
letter-spacing: 0.06em;
126+
text-transform: uppercase;
127+
}
128+
129+
.subtitle {
130+
margin: 0;
131+
color: rgba(248, 250, 252, 0.76);
132+
font-size: 13px;
133+
}
134+
135+
.spinner {
136+
width: 22px;
137+
height: 22px;
138+
border: 2px solid rgba(248, 250, 252, 0.22);
139+
border-top-color: #f8fafc;
140+
border-radius: 50%;
141+
animation: spin 0.9s linear infinite;
142+
}
143+
144+
@keyframes spin {
145+
to {
146+
transform: rotate(360deg);
147+
}
148+
}
149+
</style>
150+
</head>
151+
<body>
152+
<div class="panel" aria-label="Loading Code Release Tracker">
153+
<div class="spinner" aria-hidden="true"></div>
154+
<p class="title">Code Release Tracker</p>
155+
<p class="subtitle">Loading releases and workspace state...</p>
156+
</div>
157+
</body>
158+
</html>
159+
`)}`,
160+
);
161+
162+
return splash;
163+
}
164+
15165
function createWindow() {
16166
const window = new BrowserWindow({
17167
width: 1440,
@@ -24,6 +174,7 @@ function createWindow() {
24174
frame: false,
25175
titleBarStyle: "hidden",
26176
autoHideMenuBar: true,
177+
show: false,
27178
webPreferences: {
28179
preload: path.join(__dirname, "preload.js"),
29180
nodeIntegration: false,
@@ -44,6 +195,10 @@ function createWindow() {
44195
window.webContents.send("window:maximized-changed", false);
45196
});
46197

198+
window.once("ready-to-show", () => {
199+
window.show();
200+
});
201+
47202
if (isDev) {
48203
window.loadURL("http://localhost:5173");
49204
} else {
@@ -55,6 +210,19 @@ function createWindow() {
55210
return window;
56211
}
57212

213+
function launchApplicationWindow() {
214+
const splashWindow = createSplashWindow();
215+
const mainWindow = createWindow();
216+
217+
mainWindow.once("ready-to-show", () => {
218+
if (!splashWindow.isDestroyed()) {
219+
splashWindow.close();
220+
}
221+
});
222+
223+
return mainWindow;
224+
}
225+
58226
app.whenReady().then(() => {
59227
ipcMain.on("window:minimize", (event) => {
60228
BrowserWindow.fromWebContents(event.sender)?.minimize();
@@ -75,11 +243,12 @@ app.whenReady().then(() => {
75243
BrowserWindow.fromWebContents(event.sender)?.close();
76244
});
77245

78-
createWindow();
246+
launchApplicationWindow();
247+
setupAutoUpdater();
79248

80249
app.on("activate", () => {
81250
if (BrowserWindow.getAllWindows().length === 0) {
82-
createWindow();
251+
launchApplicationWindow();
83252
}
84253
});
85254
});

0 commit comments

Comments
 (0)