Compare commits
1
Commits
latest-manifest
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2248655b |
Generated
+20
@@ -10,6 +10,8 @@
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
"@tauri-apps/plugin-dialog": "^2.7.2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.4.0",
|
||||
"lucide-react": "^1.27.0",
|
||||
@@ -1500,6 +1502,24 @@
|
||||
"@tauri-apps/api": "^2.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-process": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz",
|
||||
"integrity": "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/plugin-updater": {
|
||||
"version": "2.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz",
|
||||
"integrity": "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==",
|
||||
"license": "MIT OR Apache-2.0",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.10.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz",
|
||||
|
||||
+3
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "mail-client",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"version": "0.1.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,6 +13,8 @@
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.11.1",
|
||||
"@tauri-apps/plugin-dialog": "^2.7.2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^4.4.0",
|
||||
"lucide-react": "^1.27.0",
|
||||
|
||||
Executable
+173
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env bash
|
||||
# Builds, signs, and publishes a release to Gitea (git.kanjiv.at/KanjiG/mail-client).
|
||||
#
|
||||
# Usage: scripts/release.sh <version> [notes]
|
||||
# scripts/release.sh 0.2.0 "Fixed X, added Y"
|
||||
#
|
||||
# Requires:
|
||||
# - TAURI_SIGNING_PRIVATE_KEY_PATH pointing at the updater signing key
|
||||
# (default: ~/.tauri/mail-client-update.key)
|
||||
# - GITEA_TOKEN or GITEA_PASSWORD env var for authenticating to the Gitea API
|
||||
# (GITEA_TOKEN preferred — a Gitea access token instead of the account password)
|
||||
#
|
||||
# Only builds for the platform it's run on. To publish Windows/macOS builds,
|
||||
# run this same script on/from those platforms with the same version — it
|
||||
# appends to the same release rather than overwriting it, and regenerates
|
||||
# latest.json to include whichever platform artifacts are present so far.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
VERSION="${1:?Usage: scripts/release.sh <version> [notes]}"
|
||||
NOTES="${2:-Release $VERSION}"
|
||||
REPO_OWNER="KanjiG"
|
||||
REPO_NAME="mail-client"
|
||||
GITEA_URL="https://git.kanjiv.at"
|
||||
TAG="v$VERSION"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PATH="${TAURI_SIGNING_PRIVATE_KEY_PATH:-$HOME/.tauri/mail-client-update.key}"
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="${TAURI_SIGNING_PRIVATE_KEY_PASSWORD:-}"
|
||||
|
||||
if [ -z "${GITEA_TOKEN:-}" ] && [ -z "${GITEA_PASSWORD:-}" ]; then
|
||||
echo "Set GITEA_TOKEN (preferred) or GITEA_PASSWORD before running this." >&2
|
||||
exit 1
|
||||
fi
|
||||
AUTH_HEADER=()
|
||||
if [ -n "${GITEA_TOKEN:-}" ]; then
|
||||
AUTH_HEADER=(-H "Authorization: token $GITEA_TOKEN")
|
||||
else
|
||||
AUTH_HEADER=(-u "$REPO_OWNER:$GITEA_PASSWORD")
|
||||
fi
|
||||
|
||||
echo "==> Syncing version to $VERSION in package.json / tauri.conf.json"
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
for (const f of ['package.json', 'src-tauri/tauri.conf.json']) {
|
||||
const j = JSON.parse(fs.readFileSync(f, 'utf8'));
|
||||
j.version = '$VERSION';
|
||||
fs.writeFileSync(f, JSON.stringify(j, null, 2) + '\n');
|
||||
}
|
||||
"
|
||||
sed -i "s/^version = \".*\"/version = \"$VERSION\"/" src-tauri/Cargo.toml
|
||||
|
||||
echo "==> Building frontend"
|
||||
npm run build
|
||||
|
||||
echo "==> Building + signing Tauri bundle"
|
||||
npx tauri build
|
||||
|
||||
BUNDLE_DIR="src-tauri/target/release/bundle"
|
||||
|
||||
# Figure out which platform we're on and where the updater-relevant artifact +
|
||||
# signature landed. Tauri's updater only consumes specific bundle formats per
|
||||
# platform (AppImage on Linux, NSIS on Windows, .app.tar.gz on macOS) — not
|
||||
# .deb/.rpm, those are for manual installs only.
|
||||
UNAME="$(uname -s)"
|
||||
PLATFORM_KEY=""
|
||||
ARTIFACT=""
|
||||
case "$UNAME" in
|
||||
Linux)
|
||||
PLATFORM_KEY="linux-x86_64"
|
||||
ARTIFACT="$(find "$BUNDLE_DIR/appimage" -name '*.AppImage' | head -1)"
|
||||
;;
|
||||
Darwin)
|
||||
ARCH="$(uname -m)"
|
||||
PLATFORM_KEY="darwin-$([ "$ARCH" = "arm64" ] && echo aarch64 || echo x86_64)"
|
||||
ARTIFACT="$(find "$BUNDLE_DIR/macos" -name '*.app.tar.gz' | head -1)"
|
||||
;;
|
||||
MINGW*|MSYS*|CYGWIN*)
|
||||
PLATFORM_KEY="windows-x86_64"
|
||||
ARTIFACT="$(find "$BUNDLE_DIR/nsis" -name '*-setup.exe' | head -1)"
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognized platform: $UNAME" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$ARTIFACT" ] || [ ! -f "$ARTIFACT" ]; then
|
||||
echo "Could not find the updater artifact for $PLATFORM_KEY under $BUNDLE_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
SIGNATURE="$(cat "$ARTIFACT.sig")"
|
||||
FILENAME="$(basename "$ARTIFACT")"
|
||||
|
||||
echo "==> Ensuring release $TAG exists on Gitea"
|
||||
RELEASE_JSON="$(curl -s "${AUTH_HEADER[@]}" "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$TAG")"
|
||||
RELEASE_ID="$(echo "$RELEASE_JSON" | node -pe 'JSON.parse(require("fs").readFileSync(0)).id' 2>/dev/null || echo "")"
|
||||
if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "undefined" ]; then
|
||||
RELEASE_JSON="$(curl -s "${AUTH_HEADER[@]}" -X POST "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(node -pe "JSON.stringify({tag_name: '$TAG', name: '$TAG', body: process.argv[1]})" "$NOTES")")
|
||||
RELEASE_ID="$(echo "$RELEASE_JSON" | node -pe 'JSON.parse(require("fs").readFileSync(0)).id')"
|
||||
fi
|
||||
echo " release id: $RELEASE_ID"
|
||||
|
||||
echo "==> Uploading $FILENAME"
|
||||
curl -s "${AUTH_HEADER[@]}" -X POST \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/$RELEASE_ID/assets?name=$FILENAME" \
|
||||
-F "attachment=@$ARTIFACT" > /dev/null
|
||||
|
||||
# latest.json lives on a SEPARATE, fixed-tag release ("latest-manifest"),
|
||||
# independent of the versioned release tags above. The updater's endpoint in
|
||||
# tauri.conf.json points at this fixed tag, not at "latest" — Gitea's support
|
||||
# for a GitHub-style /releases/latest/download/ alias varies by version, so a
|
||||
# fixed tag we fully control is more robust than depending on that.
|
||||
MANIFEST_TAG="latest-manifest"
|
||||
echo "==> Ensuring $MANIFEST_TAG release exists"
|
||||
MANIFEST_RELEASE_JSON="$(curl -s "${AUTH_HEADER[@]}" "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/tags/$MANIFEST_TAG")"
|
||||
MANIFEST_RELEASE_ID="$(echo "$MANIFEST_RELEASE_JSON" | node -pe 'JSON.parse(require("fs").readFileSync(0)).id' 2>/dev/null || echo "")"
|
||||
if [ -z "$MANIFEST_RELEASE_ID" ] || [ "$MANIFEST_RELEASE_ID" = "undefined" ]; then
|
||||
MANIFEST_RELEASE_JSON="$(curl -s "${AUTH_HEADER[@]}" -X POST "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$(node -pe "JSON.stringify({tag_name: process.argv[1], name: 'Update manifest (do not download directly)', body: 'Holds latest.json for the in-app updater. Always points at the newest release.'})" "$MANIFEST_TAG")")
|
||||
MANIFEST_RELEASE_ID="$(echo "$MANIFEST_RELEASE_JSON" | node -pe 'JSON.parse(require("fs").readFileSync(0)).id')"
|
||||
fi
|
||||
|
||||
echo "==> Fetching existing latest.json (if any) to merge platforms"
|
||||
EXISTING_LATEST="$(curl -sf "$GITEA_URL/$REPO_OWNER/$REPO_NAME/releases/download/$MANIFEST_TAG/latest.json" || echo '{}')"
|
||||
|
||||
TMP_LATEST="$(mktemp)"
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
let existing = {};
|
||||
try { existing = JSON.parse(\`$EXISTING_LATEST\`); } catch { existing = {}; }
|
||||
const platforms = existing.platforms || {};
|
||||
platforms['$PLATFORM_KEY'] = {
|
||||
signature: \`$SIGNATURE\`,
|
||||
url: '$GITEA_URL/$REPO_OWNER/$REPO_NAME/releases/download/$TAG/$FILENAME',
|
||||
};
|
||||
const manifest = {
|
||||
version: '$VERSION',
|
||||
notes: $(node -pe "JSON.stringify(process.argv[1])" "$NOTES"),
|
||||
pub_date: new Date().toISOString(),
|
||||
platforms,
|
||||
};
|
||||
fs.writeFileSync('$TMP_LATEST', JSON.stringify(manifest, null, 2));
|
||||
"
|
||||
|
||||
# Remove any previous latest.json asset before re-uploading (Gitea won't
|
||||
# overwrite an existing asset with the same name).
|
||||
EXISTING_ASSET_ID="$(curl -s "${AUTH_HEADER[@]}" "$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/$MANIFEST_RELEASE_ID" \
|
||||
| node -pe "
|
||||
const rel = JSON.parse(require('fs').readFileSync(0));
|
||||
const a = (rel.assets || []).find(x => x.name === 'latest.json');
|
||||
a ? a.id : ''
|
||||
" 2>/dev/null || echo "")"
|
||||
if [ -n "$EXISTING_ASSET_ID" ] && [ "$EXISTING_ASSET_ID" != "undefined" ]; then
|
||||
curl -s "${AUTH_HEADER[@]}" -X DELETE \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/$MANIFEST_RELEASE_ID/assets/$EXISTING_ASSET_ID" > /dev/null
|
||||
fi
|
||||
|
||||
echo "==> Uploading latest.json to $MANIFEST_TAG"
|
||||
curl -s "${AUTH_HEADER[@]}" -X POST \
|
||||
"$GITEA_URL/api/v1/repos/$REPO_OWNER/$REPO_NAME/releases/$MANIFEST_RELEASE_ID/assets?name=latest.json" \
|
||||
-F "attachment=@$TMP_LATEST" > /dev/null
|
||||
rm -f "$TMP_LATEST"
|
||||
|
||||
echo "==> Done."
|
||||
echo " Release: $GITEA_URL/$REPO_OWNER/$REPO_NAME/releases/tag/$TAG"
|
||||
echo " Manifest URL: $GITEA_URL/$REPO_OWNER/$REPO_NAME/releases/download/$MANIFEST_TAG/latest.json"
|
||||
Generated
+272
@@ -117,6 +117,8 @@ dependencies = [
|
||||
"tauri-plugin-dialog",
|
||||
"tauri-plugin-log",
|
||||
"tauri-plugin-notification",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-updater",
|
||||
"thiserror 1.0.69",
|
||||
"tokio",
|
||||
"uuid",
|
||||
@@ -134,6 +136,15 @@ dependencies = [
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
@@ -815,6 +826,17 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "2.1.1"
|
||||
@@ -1155,6 +1177,16 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.9"
|
||||
@@ -1757,6 +1789,21 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
@@ -2372,6 +2419,12 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
@@ -2748,6 +2801,18 @@ dependencies = [
|
||||
"objc2-core-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-osa-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"objc2",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.3.2"
|
||||
@@ -2870,6 +2935,20 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "osakit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||
dependencies = [
|
||||
"objc2",
|
||||
"objc2-foundation",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ouroboros"
|
||||
version = "0.18.5"
|
||||
@@ -3380,15 +3459,20 @@ dependencies = [
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"rustls-platform-verifier",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
@@ -3424,6 +3508,20 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"getrandom 0.2.17",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rusqlite"
|
||||
version = "0.31.0"
|
||||
@@ -3466,6 +3564,32 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-native-certs"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d"
|
||||
dependencies = [
|
||||
"openssl-probe",
|
||||
"rustls-pki-types",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.15.1"
|
||||
@@ -3475,6 +3599,44 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
||||
dependencies = [
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"jni 0.22.4",
|
||||
"log",
|
||||
"once_cell",
|
||||
"rustls",
|
||||
"rustls-native-certs",
|
||||
"rustls-platform-verifier-android",
|
||||
"rustls-webpki",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"webpki-root-certs",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-platform-verifier-android"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.23"
|
||||
@@ -4103,6 +4265,17 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
@@ -4320,6 +4493,49 @@ dependencies = [
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-process"
|
||||
version = "2.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a"
|
||||
dependencies = [
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
"flate2",
|
||||
"futures-util",
|
||||
"http",
|
||||
"infer",
|
||||
"log",
|
||||
"minisign-verify",
|
||||
"osakit",
|
||||
"percent-encoding",
|
||||
"reqwest 0.13.4",
|
||||
"rustls",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.19",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.60.2",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.11.3"
|
||||
@@ -4588,6 +4804,16 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.19"
|
||||
@@ -4901,6 +5127,12 @@ version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.8"
|
||||
@@ -5166,6 +5398,15 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.38.2"
|
||||
@@ -5409,6 +5650,15 @@ dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -5751,6 +6001,16 @@ dependencies = [
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
@@ -5932,6 +6192,18 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"indexmap 2.14.0",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.23"
|
||||
|
||||
@@ -25,6 +25,8 @@ tauri = { version = "2.11.3", features = [] }
|
||||
tauri-plugin-log = "2"
|
||||
tauri-plugin-dialog = "2"
|
||||
tauri-plugin-notification = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
imap = "3.0.0-alpha.15"
|
||||
native-tls = "0.2"
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
"notification:default",
|
||||
"core:webview:allow-create-webview-window",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-set-focus"
|
||||
"core:window:allow-set-focus",
|
||||
"updater:default",
|
||||
"process:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
.plugin(tauri_plugin_notification::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.setup(|app| {
|
||||
if cfg!(debug_assertions) {
|
||||
app.handle().plugin(
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"createUpdaterArtifacts": true,
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
@@ -38,5 +39,13 @@
|
||||
"android": {
|
||||
"debugApplicationIdSuffix": ".debug"
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDZCQTc0QThDNzI0RURGODAKUldTQTMwNXlqRXFuYXdsbm5nMUhBMVpzUVRxdTlzcDUvS3g1SGdHSjFPNkh3cW1rSUN5MVlyMkQK",
|
||||
"endpoints": [
|
||||
"https://git.kanjiv.at/KanjiG/mail-client/releases/download/latest-manifest/latest.json"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
@@ -45,6 +45,7 @@ import { api } from './lib/api'
|
||||
import { parseAddressList } from './lib/addresses'
|
||||
import { escapeHtml } from './lib/richtext'
|
||||
import { loadAppSettings, saveAppSettings, type AppSettings } from './lib/settings'
|
||||
import { checkForUpdate, installUpdate } from './lib/updater'
|
||||
import {
|
||||
applyTheme,
|
||||
loadConversationsEnabled,
|
||||
@@ -253,6 +254,11 @@ export default function App() {
|
||||
setTimeout(() => setToast(null), 3500)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
handleCheckForUpdates(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
applyTheme(theme)
|
||||
if (theme !== 'system') return
|
||||
@@ -443,6 +449,25 @@ export default function App() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [view, selectedMessage])
|
||||
|
||||
async function handleCheckForUpdates(silent = false) {
|
||||
const update = await checkForUpdate()
|
||||
if (!update) {
|
||||
if (!silent) showToast('Du hast schon die neueste Version')
|
||||
return
|
||||
}
|
||||
if (!window.confirm(`Version ${update.version} ist verfügbar (aktuell: ${update.currentVersion}).\n\n${update.body ?? ''}\n\nJetzt installieren? Die App startet danach neu.`)) {
|
||||
return
|
||||
}
|
||||
showToast('Update wird heruntergeladen…')
|
||||
try {
|
||||
await installUpdate(update, (pct) => {
|
||||
if (pct !== null) showToast(`Update wird installiert… ${pct}%`)
|
||||
})
|
||||
} catch (e) {
|
||||
showToast(`Update fehlgeschlagen: ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
if (offlineMode) {
|
||||
showToast('Offline-Modus aktiv')
|
||||
@@ -1818,6 +1843,7 @@ export default function App() {
|
||||
onSyncGoogleCalendar={handleSyncGoogleCalendar}
|
||||
onReconnectGoogleAccount={handleReconnectGoogleAccount}
|
||||
onOpenCaldav={() => setCaldavModalOpen(true)}
|
||||
onCheckForUpdates={handleCheckForUpdates}
|
||||
theme={theme}
|
||||
onThemeChange={handleThemeChange}
|
||||
onOpenSettings={() => setSettingsModalOpen(true)}
|
||||
|
||||
@@ -273,6 +273,7 @@ export function Ribbon({
|
||||
onSyncGoogleCalendar,
|
||||
onReconnectGoogleAccount,
|
||||
onOpenCaldav,
|
||||
onCheckForUpdates,
|
||||
theme,
|
||||
onThemeChange,
|
||||
onOpenSettings,
|
||||
@@ -337,6 +338,7 @@ export function Ribbon({
|
||||
onSyncGoogleCalendar?: (accountId: string) => Promise<void>
|
||||
onReconnectGoogleAccount?: (accountId: string) => Promise<void>
|
||||
onOpenCaldav?: () => void
|
||||
onCheckForUpdates: () => void
|
||||
theme: Theme
|
||||
onThemeChange: (t: Theme) => void
|
||||
onOpenSettings: () => void
|
||||
@@ -556,6 +558,7 @@ export function Ribbon({
|
||||
<ClassicRibbonGroup caption="Support">
|
||||
<HelpMenu onShowShortcuts={onShowShortcuts} />
|
||||
<AboutMenu />
|
||||
<ClassicButton icon={RefreshCw} label="Nach Updates suchen" onClick={onCheckForUpdates} />
|
||||
</ClassicRibbonGroup>
|
||||
)}
|
||||
</ClassicRibbonBody>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { check, type Update } from '@tauri-apps/plugin-updater'
|
||||
import { relaunch } from '@tauri-apps/plugin-process'
|
||||
|
||||
export async function checkForUpdate(): Promise<Update | null> {
|
||||
try {
|
||||
return await check()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function installUpdate(update: Update, onProgress: (pct: number | null) => void): Promise<void> {
|
||||
let total = 0
|
||||
let downloaded = 0
|
||||
await update.downloadAndInstall((event) => {
|
||||
if (event.event === 'Started') {
|
||||
total = event.data.contentLength ?? 0
|
||||
onProgress(total > 0 ? 0 : null)
|
||||
} else if (event.event === 'Progress') {
|
||||
downloaded += event.data.chunkLength
|
||||
onProgress(total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : null)
|
||||
} else if (event.event === 'Finished') {
|
||||
onProgress(100)
|
||||
}
|
||||
})
|
||||
await relaunch()
|
||||
}
|
||||
Reference in New Issue
Block a user