Windows updates come from the Kestrove website; version 1.2.1
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -63,15 +63,21 @@ XWIN_ACCEPT_LICENSE=1 scripts/windows.sh
|
|||||||
`src-tauri/Cargo.toml`, tag the commit (`git tag v<version> && git push origin v<version>`), then:
|
`src-tauri/Cargo.toml`, tag the commit (`git tag v<version> && git push origin v<version>`), then:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
GITEA_TOKEN=<token with write:repository> TAURI_SIGNING_PRIVATE_KEY_PATH=<updater key> scripts/gitea-release.sh
|
GITEA_TOKEN=<token with write:repository> scripts/gitea-release.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
It creates the release for the tag and uploads the installers, the browser helpers and a `SHA256SUMS` file.
|
It creates the release for the tag and uploads the installers, the browser helpers and a `SHA256SUMS` file.
|
||||||
|
|
||||||
**Offer it as an update (Windows):** the script also signs the installer with the updater key, uploads the
|
**Put it on the website and offer it as an update (Windows):**
|
||||||
signature, and writes `updater/latest.json`. Sign the installer with the code-signing certificate first, because
|
|
||||||
the updater signature covers the final file. Installed copies are offered the update once that file is committed
|
```bash
|
||||||
and pushed to main; the script prints the commands. Keep the updater key and its password offline and backed up:
|
TAURI_SIGNING_PRIVATE_KEY_PATH=<updater key> scripts/publish-update.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This signs the installer with the updater key, copies it to `downloads/` in `kestrove-website` next to this repo,
|
||||||
|
and writes `updates/latest.json` there. Sign the installer with the code-signing certificate first, because the
|
||||||
|
updater signature covers the final file. Installed copies are offered the update once the website is deployed.
|
||||||
|
Keep the updater key and its password offline and backed up:
|
||||||
the app only accepts updates signed with it, so losing it means existing installs can't update.
|
the app only accepts updates signed with it, so losing it means existing installs can't update.
|
||||||
|
|
||||||
## How the browser connection works
|
## How the browser connection works
|
||||||
@@ -93,7 +99,7 @@ browser extension ─(native messaging)─▶ kestrove-nmh ─(local connection)
|
|||||||
## Updates
|
## Updates
|
||||||
|
|
||||||
On Windows, Kestrove checks for a new version a minute after it starts and once a day. The check is a plain request
|
On Windows, Kestrove checks for a new version a minute after it starts and once a day. The check is a plain request
|
||||||
for `updater/latest.json` on the Kestrove Gitea: it reveals this computer's IP address and app version, and nothing
|
for `updates/latest.json` on the Kestrove website: it reveals this computer's IP address and app version, and nothing
|
||||||
from your vault. A new version appears as a banner and installs only when you choose **Update and restart**. The app
|
from your vault. A new version appears as a banner and installs only when you choose **Update and restart**. The app
|
||||||
verifies the download's signature before running it. Turn checks off in **Settings → Updates**. On Linux, updates
|
verifies the download's signature before running it. Turn checks off in **Settings → Updates**. On Linux, updates
|
||||||
come through your package manager.
|
come through your package manager.
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "kestrove-desktop",
|
"name": "kestrove-desktop",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "kestrove-desktop",
|
"name": "kestrove-desktop",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "2.12.3"
|
"@tauri-apps/api": "2.12.3"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "kestrove-desktop",
|
"name": "kestrove-desktop",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|||||||
@@ -1,17 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Creates (or reuses) the Gitea release for this version, uploads the desktop bundles to it, and writes
|
# Creates (or reuses) the Gitea release for this version and uploads the desktop bundles to it. The website's
|
||||||
# updater/latest.json, which the Windows app checks for updates (src-tauri/src/updates.rs).
|
# downloads and the Windows app's updates come from scripts/publish-update.sh instead.
|
||||||
#
|
#
|
||||||
# GITEA_TOKEN=… TAURI_SIGNING_PRIVATE_KEY_PATH=… scripts/gitea-release.sh [dir-with-files] (default ../release/desktop)
|
# GITEA_TOKEN=… scripts/gitea-release.sh [dir-with-files] (default ../release/desktop)
|
||||||
#
|
#
|
||||||
# GITEA_TOKEN: a Gitea access token with the write:repository scope (Settings → Applications). The tag
|
# GITEA_TOKEN: a Gitea access token with the write:repository scope (Settings → Applications). The tag
|
||||||
# v<version> must already be pushed. Files: *.AppImage, *.deb, *.rpm, *-setup.exe, kestrove-nmh-*, SHA256SUMS.
|
# v<version> must already be pushed. Files: *.AppImage, *.deb, *.rpm, *-setup.exe, kestrove-nmh-*, SHA256SUMS.
|
||||||
#
|
|
||||||
# Updates: the Windows installer needs an updater signature (<installer>.sig) from the Kestrove updater key, whose
|
|
||||||
# public half is in src-tauri/tauri.conf.json. Sign after Authenticode signing: the signature covers the final
|
|
||||||
# file. With TAURI_SIGNING_PRIVATE_KEY_PATH set, a missing .sig is made here (the key's password is asked for, or
|
|
||||||
# read from TAURI_SIGNING_PRIVATE_KEY_PASSWORD). Optional NOTES: the text shown with the update.
|
|
||||||
# Users get the update only once updater/latest.json is committed and pushed to main (printed at the end).
|
|
||||||
set -eu
|
set -eu
|
||||||
cd "$(dirname "$0")/.."
|
cd "$(dirname "$0")/.."
|
||||||
: "${GITEA_TOKEN:?set GITEA_TOKEN (Gitea → Settings → Applications → token with write:repository)}"
|
: "${GITEA_TOKEN:?set GITEA_TOKEN (Gitea → Settings → Applications → token with write:repository)}"
|
||||||
@@ -21,15 +15,6 @@ DIR=${1:-../release/desktop}
|
|||||||
V=$(node -p 'require("./package.json").version')
|
V=$(node -p 'require("./package.json").version')
|
||||||
TAG="v$V"
|
TAG="v$V"
|
||||||
AUTH="Authorization: token $GITEA_TOKEN"
|
AUTH="Authorization: token $GITEA_TOKEN"
|
||||||
SETUP="$DIR/Kestrove_${V}_x64-setup.exe"
|
|
||||||
|
|
||||||
if [ -f "$SETUP" ] && [ ! -f "$SETUP.sig" ]; then
|
|
||||||
: "${TAURI_SIGNING_PRIVATE_KEY_PATH:?$SETUP.sig is missing: set TAURI_SIGNING_PRIVATE_KEY_PATH to the updater key to sign it}"
|
|
||||||
# --app-version binds the signature to this version (the app requires it), so an older signed installer can't
|
|
||||||
# be offered again under a newer version number.
|
|
||||||
npx tauri signer sign -f "$TAURI_SIGNING_PRIVATE_KEY_PATH" ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD+-p "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \
|
|
||||||
--app-version "$V" "$SETUP"
|
|
||||||
fi
|
|
||||||
|
|
||||||
(cd "$DIR" && rm -f SHA256SUMS && for f in *.AppImage *.deb *.rpm *-setup.exe kestrove-nmh-*; do if [ -f "$f" ]; then shasum -a 256 -- "$f"; fi; done > SHA256SUMS; cat SHA256SUMS)
|
(cd "$DIR" && rm -f SHA256SUMS && for f in *.AppImage *.deb *.rpm *-setup.exe kestrove-nmh-*; do if [ -f "$f" ]; then shasum -a 256 -- "$f"; fi; done > SHA256SUMS; cat SHA256SUMS)
|
||||||
|
|
||||||
@@ -45,16 +30,3 @@ for f in "$DIR"/*.AppImage "$DIR"/*.deb "$DIR"/*.rpm "$DIR"/*-setup.exe "$DIR"/*
|
|||||||
done
|
done
|
||||||
echo "Release: ${API%/api/v1}/$REPO/releases/tag/$TAG"
|
echo "Release: ${API%/api/v1}/$REPO/releases/tag/$TAG"
|
||||||
|
|
||||||
# The update manifest, written only after the installer it points to is uploaded.
|
|
||||||
if [ -f "$SETUP.sig" ]; then
|
|
||||||
mkdir -p updater
|
|
||||||
node -e '
|
|
||||||
const [v, url, sigFile, notes] = process.argv.slice(1);
|
|
||||||
const signature = require("fs").readFileSync(sigFile, "utf8").trim();
|
|
||||||
const doc = { version: v, notes, pub_date: new Date().toISOString().replace(/\.\d+Z$/, "Z"),
|
|
||||||
platforms: { "windows-x86_64": { signature, url } } };
|
|
||||||
require("fs").writeFileSync("updater/latest.json", JSON.stringify(doc, null, 2) + "\n");
|
|
||||||
' "$V" "${API%/api/v1}/$REPO/releases/download/$TAG/$(basename "$SETUP")" "$SETUP.sig" "${NOTES:-Kestrove $V}"
|
|
||||||
echo "Wrote updater/latest.json. To offer the update, commit it and push to main:"
|
|
||||||
echo " git add updater/latest.json && git commit -m \"Offer $V as an update\" && git push origin main"
|
|
||||||
fi
|
|
||||||
|
|||||||
Executable
+37
@@ -0,0 +1,37 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# Puts the Windows installer on the Kestrove website and offers it as an update: signs it with the updater key if
|
||||||
|
# needed, copies it to the website's downloads/, and writes the website's updates/latest.json, which the app checks
|
||||||
|
# (src-tauri/src/updates.rs).
|
||||||
|
#
|
||||||
|
# TAURI_SIGNING_PRIVATE_KEY_PATH=… scripts/publish-update.sh [dir-with-installer] (default ../release/desktop)
|
||||||
|
#
|
||||||
|
# Sign after Authenticode signing: the updater signature covers the final file. The key's password is asked for,
|
||||||
|
# or read from TAURI_SIGNING_PRIVATE_KEY_PASSWORD. Optional NOTES: the text shown with the update. Optional
|
||||||
|
# WEBSITE_DIR: the website folder (default ../kestrove-website). Users get the update once the website is deployed.
|
||||||
|
set -eu
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
DIR=${1:-../release/desktop}
|
||||||
|
SITE=${WEBSITE_DIR:-../kestrove-website}
|
||||||
|
V=$(node -p 'require("./package.json").version')
|
||||||
|
NAME="Kestrove_${V}_x64-setup.exe"
|
||||||
|
SETUP="$DIR/$NAME"
|
||||||
|
[ -f "$SETUP" ] || { echo "$SETUP not found" >&2; exit 1; }
|
||||||
|
|
||||||
|
if [ ! -f "$SETUP.sig" ]; then
|
||||||
|
: "${TAURI_SIGNING_PRIVATE_KEY_PATH:?$SETUP.sig is missing: set TAURI_SIGNING_PRIVATE_KEY_PATH to the updater key to sign it}"
|
||||||
|
# --app-version binds the signature to this version (the app requires it), so an older signed installer can't
|
||||||
|
# be offered again under a newer version number.
|
||||||
|
npx tauri signer sign -f "$TAURI_SIGNING_PRIVATE_KEY_PATH" ${TAURI_SIGNING_PRIVATE_KEY_PASSWORD+-p "$TAURI_SIGNING_PRIVATE_KEY_PASSWORD"} \
|
||||||
|
--app-version "$V" "$SETUP"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$SITE/downloads" "$SITE/updates"
|
||||||
|
cp "$SETUP" "$SITE/downloads/$NAME"
|
||||||
|
node -e '
|
||||||
|
const [out, v, url, sigFile, notes] = process.argv.slice(1);
|
||||||
|
const signature = require("fs").readFileSync(sigFile, "utf8").trim();
|
||||||
|
const doc = { version: v, notes, pub_date: new Date().toISOString().replace(/\.\d+Z$/, "Z"),
|
||||||
|
platforms: { "windows-x86_64": { signature, url } } };
|
||||||
|
require("fs").writeFileSync(out, JSON.stringify(doc, null, 2) + "\n");
|
||||||
|
' "$SITE/updates/latest.json" "$V" "https://kestrove.samialabri.com/downloads/$NAME" "$SETUP.sig" "${NOTES:-Kestrove $V}"
|
||||||
|
echo "Copied $NAME to $SITE/downloads and wrote $SITE/updates/latest.json. Deploy the website to offer the update."
|
||||||
Generated
+1
-1
@@ -3064,7 +3064,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kestrove-desktop"
|
name = "kestrove-desktop"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arboard",
|
"arboard",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kestrove-desktop"
|
name = "kestrove-desktop"
|
||||||
version = "1.2.0"
|
version = "1.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
rust-version = "1.82"
|
rust-version = "1.82"
|
||||||
license = "Apache-2.0"
|
license = "Apache-2.0"
|
||||||
@@ -73,7 +73,7 @@ windows-sys = { version = "0.61", features = [
|
|||||||
"Win32_UI_Input_KeyboardAndMouse",
|
"Win32_UI_Input_KeyboardAndMouse",
|
||||||
"Win32_UI_WindowsAndMessaging",
|
"Win32_UI_WindowsAndMessaging",
|
||||||
] }
|
] }
|
||||||
# In-app updates (updates.rs): signed installers listed in latest.json on the Kestrove Gitea.
|
# In-app updates (updates.rs): signed installers listed in latest.json on the Kestrove website.
|
||||||
tauri-plugin-updater = "2"
|
tauri-plugin-updater = "2"
|
||||||
# Windows Hello (KeyCredentialManager); the same version Tauri already pulls in.
|
# Windows Hello (KeyCredentialManager); the same version Tauri already pulls in.
|
||||||
windows = { version = "0.62", features = ["Foundation", "Security_Credentials", "Security_Cryptography", "Storage_Streams"] }
|
windows = { version = "0.62", features = ["Foundation", "Security_Credentials", "Security_Cryptography", "Storage_Streams"] }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! In-app updates on Windows. The Microsoft Store doesn't update EXE installs, so the app does it itself.
|
//! In-app updates on Windows. The Microsoft Store doesn't update EXE installs, so the app does it itself.
|
||||||
//!
|
//!
|
||||||
//! Tauri's updater fetches `latest.json` from the first address in tauri.conf.json → plugins.updater.endpoints
|
//! Tauri's updater fetches `latest.json` from the first address in tauri.conf.json → plugins.updater.endpoints
|
||||||
//! that answers (the Kestrove Gitea; a mirror can be added after it), and offers the newer version. Nothing is
|
//! that answers (the Kestrove website; a mirror can be added after it), and offers the newer version. Nothing is
|
||||||
//! downloaded until the user chooses "Update and restart". The installer must carry a signature from the
|
//! downloaded until the user chooses "Update and restart". The installer must carry a signature from the
|
||||||
//! Kestrove updater key, whose public half is in tauri.conf.json, so a compromised download server can hold
|
//! Kestrove updater key, whose public half is in tauri.conf.json, so a compromised download server can hold
|
||||||
//! updates back but can't ship one.
|
//! updates back but can't ship one.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://schema.tauri.app/config/2",
|
"$schema": "https://schema.tauri.app/config/2",
|
||||||
"productName": "Kestrove",
|
"productName": "Kestrove",
|
||||||
"version": "1.2.0",
|
"version": "1.2.1",
|
||||||
"identifier": "app.kestrove.desktop",
|
"identifier": "app.kestrove.desktop",
|
||||||
"build": {
|
"build": {
|
||||||
"beforeDevCommand": "npm run dev",
|
"beforeDevCommand": "npm run dev",
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDZFRUM4RDQ2Mzk0Rjg5Q0IKUldUTGlVODVSbzNzYml1ZTN3d0pBN1IwWkRoZXNROTRmbk5WOE5CR2VJQVYrTUJLN21PNzI4eHEK",
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDZFRUM4RDQ2Mzk0Rjg5Q0IKUldUTGlVODVSbzNzYml1ZTN3d0pBN1IwWkRoZXNROTRmbk5WOE5CR2VJQVYrTUJLN21PNzI4eHEK",
|
||||||
"requireSignedVersion": true,
|
"requireSignedVersion": true,
|
||||||
"endpoints": [
|
"endpoints": [
|
||||||
"https://gitea.samialabri.com/kestrove/kestrove-desktop/raw/branch/main/updater/latest.json"
|
"https://kestrove.samialabri.com/updates/latest.json"
|
||||||
],
|
],
|
||||||
"windows": {
|
"windows": {
|
||||||
"installMode": "passive"
|
"installMode": "passive"
|
||||||
|
|||||||
Reference in New Issue
Block a user