Attempt installer/updating

This commit is contained in:
Julian Chang 2026-07-06 01:00:56 -04:00
parent 99d095088c
commit 9cb5db15f0
No known key found for this signature in database
20 changed files with 734 additions and 5 deletions

32
.github/actions/setup-rust/action.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Setup Rust
description: Install the Rust toolchain (+ optional targets) and restore the build cache.
inputs:
target:
description: Comma-separated extra target triples to install
required: false
default: ''
save-cache:
description: Whether to save the Rust cache
required: false
default: 'false'
restore-cache:
description: Whether to restore the Rust cache
required: false
default: 'true'
runs:
using: composite
steps:
- name: Install Rust (stable)
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: ${{ inputs.target }}
components: cargo, clippy, rustc, rustfmt
- name: Cache Rust
if: ${{ inputs.restore-cache == 'true' }}
uses: Swatinem/rust-cache@master
with:
key: ${{ inputs.target }}
save-if: ${{ inputs.save-cache }}
shared-key: release-cache

293
.github/workflows/oneclient_release.yml vendored Normal file
View file

@ -0,0 +1,293 @@
name: OneClient Release Build
on:
workflow_dispatch:
env:
CARGO_PACKAGER_SIGN_PRIVATE_KEY: ${{ secrets.CARGO_PACKAGER_SIGN_PRIVATE_KEY }}
CARGO_PACKAGER_SIGN_PRIVATE_KEY_PASSWORD: ${{ secrets.CARGO_PACKAGER_SIGN_PRIVATE_KEY_PASSWORD }}
jobs:
prepare:
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
version: ${{ steps.version.outputs.version }}
tag: ${{ steps.version.outputs.tag }}
steps:
- uses: actions/checkout@v4
- name: Resolve version from Cargo.toml
id: version
shell: bash
run: |
VERSION=$(grep -m1 '^version' Cargo.toml | sed -E 's/.*"([^"]+)".*/\1/')
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=oneclient-$VERSION" >> "$GITHUB_OUTPUT"
- name: Create/ensure draft release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.version.outputs.tag }}
VERSION: ${{ steps.version.outputs.version }}
run: |
if ! gh release view "$TAG" >/dev/null 2>&1; then
gh release create "$TAG" \
--draft \
--title "OneClient $VERSION" \
--notes "Automated release for OneClient $VERSION."
fi
# ---------------------------------------------------------------------------
package:
needs: prepare
permissions:
contents: write
# Required so SignPath can download the uploaded build artifact.
actions: read
strategy:
fail-fast: false
matrix:
include:
- platform: macos-latest
target: aarch64-apple-darwin
os_key: darwin
arch: aarch64
formats: app,dmg
- platform: macos-latest
target: x86_64-apple-darwin
os_key: darwin
arch: x86_64
formats: app,dmg
- platform: ubuntu-22.04
target: ''
os_key: linux
arch: x86_64
formats: deb,appimage
- platform: windows-latest
target: ''
os_key: windows
arch: x86_64
formats: nsis
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v4
- name: Setup Rust
uses: ./.github/actions/setup-rust
with:
target: ${{ matrix.target }}
save-cache: true
restore-cache: true
- name: (Linux) Install system dependencies
if: matrix.platform == 'ubuntu-22.04'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential cmake pkg-config clang python3 \
libfontconfig-dev libfreetype-dev \
libgl1-mesa-dev libx11-dev libxcursor-dev libxi-dev libxrandr-dev \
libxkbcommon-dev libxkbcommon-x11-dev libwayland-dev libdbus-1-dev \
libfuse2
- name: Install cargo-packager
shell: bash
run: cargo install cargo-packager --version 0.11.8 --locked
- name: Import Apple Developer certificate
if: startsWith(matrix.platform, 'macos')
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
CERT_ID=$(security find-identity -v -p codesigning build.keychain | awk -F'"' '/Developer ID Application/ {print $2; exit}')
if [ -z "$CERT_ID" ]; then
echo "::error::No Developer ID Application certificate found in keychain."
exit 1
fi
echo "APPLE_SIGNING_IDENTITY=$CERT_ID" >> "$GITHUB_ENV"
- name: Build release binary
shell: bash
run: |
if [ -n "${{ matrix.target }}" ]; then
cargo build --release --bin oneclient_app --target ${{ matrix.target }}
else
cargo build --release --bin oneclient_app
fi
- name: Package
shell: bash
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
ARGS="--release --formats ${{ matrix.formats }}"
if [ -n "${{ matrix.target }}" ]; then
ARGS="$ARGS --target ${{ matrix.target }}"
fi
cargo packager $ARGS --verbose
- name: Resolve packager output dir
id: outdir
shell: bash
run: |
if [ -n "${{ matrix.target }}" ]; then
echo "dir=target/${{ matrix.target }}/release" >> "$GITHUB_OUTPUT"
else
echo "dir=target/release" >> "$GITHUB_OUTPUT"
fi
- name: (Windows) Locate installer
if: matrix.platform == 'windows-latest'
id: win_installer
shell: bash
run: echo "path=$(ls ${{ steps.outdir.outputs.dir }}/*-setup.exe | head -n1)" >> "$GITHUB_OUTPUT"
- name: (Windows) Upload unsigned installer artifact
if: matrix.platform == 'windows-latest'
id: win_upload_unsigned
uses: actions/upload-artifact@v4
with:
name: oneclient-windows-installer-unsigned
path: ${{ steps.win_installer.outputs.path }}
- name: (Windows) Submit SignPath signing request
if: matrix.platform == 'windows-latest'
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ secrets.SIGNPATH_ORGANIZATION_ID }}
project-slug: onelauncher
signing-policy-slug: test-signing
artifact-configuration-slug: onelauncher-installer
github-artifact-id: ${{ steps.win_upload_unsigned.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: signed
- name: (Windows) Replace installer with signed one + recompute updater sig
if: matrix.platform == 'windows-latest'
shell: bash
run: |
signed="$(ls signed/*-setup.exe | head -n1)"
# Overwrite the unsigned installer in the packager out dir with the signed exe.
cp "$signed" "${{ steps.win_installer.outputs.path }}"
# Recompute the updater signature over the SIGNED exe.
cargo packager signer sign \
--private-key "$CARGO_PACKAGER_SIGN_PRIVATE_KEY" \
--password "$CARGO_PACKAGER_SIGN_PRIVATE_KEY_PASSWORD" \
"${{ steps.win_installer.outputs.path }}"
- name: Upload installers and build manifest fragment
shell: bash
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.prepare.outputs.tag }}
OUT_DIR: ${{ steps.outdir.outputs.dir }}
OS_KEY: ${{ matrix.os_key }}
ARCH: ${{ matrix.arch }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
set -euo pipefail
shopt -s nullglob
# Identify the single updater artifact for this platform (the file with a
# sibling .sig): -setup.exe (nsis), *.app.tar.gz (app), *.AppImage.tar.gz (appimage).
updater_file=""; fmt=""; ext=""
case "$OS_KEY" in
windows) updater_file="$(ls "$OUT_DIR"/*-setup.exe | head -n1)"; fmt="nsis"; ext="exe" ;;
darwin) updater_file="$(ls "$OUT_DIR"/*.app.tar.gz | head -n1)"; fmt="app"; ext="app.tar.gz" ;;
linux) updater_file="$(ls "$OUT_DIR"/*.AppImage.tar.gz 2>/dev/null | head -n1)"; fmt="appimage"; ext="AppImage.tar.gz"
if [ -z "$updater_file" ]; then updater_file="$(ls "$OUT_DIR"/*.AppImage | head -n1)"; ext="AppImage"; fi ;;
esac
# The updater bundle isn't always arch-tagged by cargo-packager, so the two
# macOS legs would collide on the release. Rename the updater artifact + its .sig
# to an arch-unique name before upload, and point the manifest at that name.
unique="OneClient_${VERSION}_${OS_KEY}_${ARCH}.${ext}"
cp "$updater_file" "$OUT_DIR/$unique"
cp "$updater_file.sig" "$OUT_DIR/$unique.sig"
# Upload human-facing installers (dmg/deb/AppImage/signed exe) + the renamed
# updater bundle and its .sig. Skip the un-renamed updater bundle to avoid clashes.
for f in "$OUT_DIR"/*.dmg "$OUT_DIR"/*.deb "$OUT_DIR"/*.AppImage "$OUT_DIR"/*-setup.exe "$OUT_DIR/$unique" "$OUT_DIR/$unique.sig"; do
[ -e "$f" ] || continue
gh release upload "$TAG" "$f" --clobber
done
base="$unique"
mkdir -p fragments
SIG="$(cat "$updater_file.sig")" \
URL="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/${base}" \
FMT="$fmt" \
KEY="${OS_KEY}-${ARCH}" \
python3 - <<'PY'
import json, os
frag = {
os.environ["KEY"]: {
"signature": os.environ["SIG"],
"url": os.environ["URL"],
"format": os.environ["FMT"],
}
}
with open(f"fragments/{os.environ['KEY']}.json", "w") as f:
json.dump(frag, f)
PY
- name: Upload manifest fragment
uses: actions/upload-artifact@v4
with:
name: manifest-${{ matrix.os_key }}-${{ matrix.arch }}
path: fragments/*.json
manifest:
needs: [prepare, package]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download all manifest fragments
uses: actions/download-artifact@v4
with:
pattern: manifest-*
path: fragments
merge-multiple: true
- name: Assemble latest.json
env:
VERSION: ${{ needs.prepare.outputs.version }}
run: |
python3 - <<'PY'
import json, glob, os, datetime
platforms = {}
for path in glob.glob("fragments/*.json"):
with open(path) as f:
platforms.update(json.load(f))
manifest = {
"version": os.environ["VERSION"],
"pub_date": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
"platforms": platforms,
}
with open("latest.json", "w") as f:
json.dump(manifest, f, indent=2)
print(json.dumps(manifest, indent=2))
PY
- name: Upload latest.json to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ needs.prepare.outputs.tag }}
run: gh release upload "$TAG" latest.json --clobber

View file

@ -55,6 +55,19 @@ The app crate is `oneclient_app`:
After you finish making your changes and committed them to your branch, run
`cargo fmt` and `cargo clippy` to fix style inconsistencies and catch lints.
### Packaging
Installers are built with [cargo-packager] (config in
`packages/oneclient_app/Cargo.toml` under `[package.metadata.packager]`):
- `cargo install cargo-packager --locked`
- `cargo build --release -p oneclient_app`
- `cargo packager --release -p oneclient_app --formats <nsis|app,dmg|deb,appimage>`
Releases are cut by dispatching the `OneClient Release Build` GitHub Actions
workflow. See the **Packaging / Releasing** section of the [README] for details
and per-OS prerequisites.
### Pull Request
Once you have finished making your changes, create a pull request (PR) to submit them.
@ -84,6 +97,7 @@ Run `xcode-select --install` in the terminal to install them. If they are alread
This CONTRIBUTING.md file was inspired by the [`github/docs` CONTRIBUTING.md] file, and we extend our gratitude to the original authors.
[cargo-packager]: https://github.com/crabnebula-dev/cargo-packager
[Skia]: https://skia.org/
[Freya setup guide]: https://book.freyaui.dev/setup.html
[`cargo`]: https://doc.rust-lang.org/cargo/getting-started/installation.html

265
Cargo.lock generated
View file

@ -931,6 +931,42 @@ dependencies = [
"wayland-client",
]
[[package]]
name = "cargo-packager-updater"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eec09acab5c2227aba2e592d431708305bdeb6d507703f6cd8983fb57b6c5ef7"
dependencies = [
"base64",
"cargo-packager-utils",
"ctor 0.2.9",
"dirs",
"dunce",
"flate2",
"http",
"log",
"minisign-verify",
"percent-encoding",
"reqwest 0.12.28",
"semver",
"serde",
"serde_json",
"tar",
"tempfile",
"thiserror 1.0.69",
"time",
"url",
]
[[package]]
name = "cargo-packager-utils"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b43458dd2ee3cdab3f5b105acd80791383b730380c929018701313d7d299d4e8"
dependencies = [
"ctor 0.2.9",
]
[[package]]
name = "cc"
version = "1.2.63"
@ -1304,6 +1340,16 @@ dependencies = [
"hybrid-array",
]
[[package]]
name = "ctor"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "ctor"
version = "0.10.1"
@ -1346,6 +1392,12 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2"
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
[[package]]
name = "digest"
version = "0.10.7"
@ -1373,7 +1425,28 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
dependencies = [
"dirs-sys",
"dirs-sys 0.5.0",
]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys 0.4.1",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users 0.4.6",
"windows-sys 0.48.0",
]
[[package]]
@ -1384,7 +1457,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"redox_users 0.5.2",
"windows-sys 0.61.2",
]
@ -2606,6 +2679,7 @@ dependencies = [
"hyper",
"hyper-util",
"rustls",
"rustls-native-certs",
"tokio",
"tokio-rustls",
"tower-service",
@ -3359,6 +3433,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"
@ -3530,6 +3610,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "num-conv"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]]
name = "num-derive"
version = "0.4.2"
@ -4020,6 +4106,7 @@ dependencies = [
"anyhow",
"arboard",
"bytes 1.11.1",
"cargo-packager-updater",
"chrono",
"freya",
"freya-skia-safe",
@ -4064,7 +4151,7 @@ dependencies = [
"parking_lot",
"polyio",
"rand 0.10.1",
"reqwest",
"reqwest 0.13.3",
"sanitize-filename",
"serde",
"serde_json",
@ -4379,6 +4466,12 @@ dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@ -4757,6 +4850,17 @@ dependencies = [
"bitflags 2.12.1",
]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom 0.2.17",
"libredox",
"thiserror 1.0.69",
]
[[package]]
name = "redox_users"
version = "0.5.2"
@ -4797,6 +4901,48 @@ version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"bytes 1.11.1",
"futures-channel",
"futures-core",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-native-certs",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tokio-util",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams 0.4.2",
"web-sys",
]
[[package]]
name = "reqwest"
version = "0.13.3"
@ -4841,7 +4987,7 @@ dependencies = [
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"wasm-streams",
"wasm-streams 0.5.0",
"web-sys",
]
@ -5868,6 +6014,36 @@ dependencies = [
"zune-jpeg",
]
[[package]]
name = "time"
version = "0.3.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50"
dependencies = [
"deranged",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "time-macros"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tiny-skia"
version = "0.11.4"
@ -5900,7 +6076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4"
dependencies = [
"as-raw-xcb-connection",
"ctor",
"ctor 0.10.1",
"libloading",
"pkg-config",
"tracing",
@ -6538,6 +6714,19 @@ dependencies = [
"wasmparser",
]
[[package]]
name = "wasm-streams"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65"
dependencies = [
"futures-util",
"js-sys",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]]
name = "wasm-streams"
version = "0.5.0"
@ -6950,6 +7139,15 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
@ -6977,6 +7175,21 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm 0.48.5",
"windows_aarch64_msvc 0.48.5",
"windows_i686_gnu 0.48.5",
"windows_i686_msvc 0.48.5",
"windows_x86_64_gnu 0.48.5",
"windows_x86_64_gnullvm 0.48.5",
"windows_x86_64_msvc 0.48.5",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
@ -7019,6 +7232,12 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
@ -7031,6 +7250,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
@ -7043,6 +7268,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
@ -7067,6 +7298,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
@ -7079,6 +7316,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
@ -7091,6 +7334,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
@ -7103,6 +7352,12 @@ version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"

View file

@ -35,6 +35,22 @@ cargo run -p oneclient_app
cargo build --release -p oneclient_app
```
### Packaging / Releasing
Installers are produced with [**cargo-packager**](https://github.com/crabnebula-dev/cargo-packager)
(the standalone bundler spun out of the Tauri bundler). Config lives in
[`packages/oneclient_app/Cargo.toml`](./packages/oneclient_app/Cargo.toml) under
`[package.metadata.packager]`.
```sh
cargo install cargo-packager --locked
# Build the binary, then bundle it for the current OS:
cargo build --release -p oneclient_app
cargo packager --release -p oneclient_app --formats <targets>
# Windows: nsis macOS: app,dmg Linux: deb,appimage
```
### Versioning
The workspace shares a single version, defined in the root [`Cargo.toml`](./Cargo.toml)

View file

@ -32,6 +32,8 @@ sysinfo.workspace = true
anyhow.workspace = true
regex.workspace = true
cargo-packager-updater = "=0.2.3"
# file dialog
rfd = "=0.17.2"
@ -48,3 +50,43 @@ mimalloc = "=0.1.52"
[patch.crates-io]
freya = { git = "https://github.com/marc2332/freya.git", branch = "fix/blur" }
# Build then package (per OS):
# cargo build --release -p oneclient_app
# cargo packager --release -p oneclient_app --formats <nsis|app,dmg|deb,appimage>
[package.metadata.packager]
product-name = "OneClient"
identifier = "org.polyfrost.OneClient"
category = "Game"
publisher = "Polyfrost"
copyright = "© 2026 Polyfrost Inc."
homepage = "https://polyfrost.org/projects/oneclient"
license-file = "../../LICENSE"
long-description = "Next-generation open source Minecraft launcher"
formats = ["nsis", "app", "dmg", "deb", "appimage"]
binaries = [{ path = "oneclient_app", main = true }]
icons = [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico",
]
[package.metadata.packager.macos]
minimum-system-version = "10.15"
entitlements = "distribution/App.entitlements"
[package.metadata.packager.dmg]
app-position = { x = 190, y = 190 }
app-folder-position = { x = 470, y = 190 }
[package.metadata.packager.windows]
digest-algorithm = "sha256"
timestamp-url = "https://timestamp.digicert.com"
[package.metadata.packager.deb]
desktop-template = "distribution/linux/org.polyfrost.oneclient.template"
[package.metadata.packager.appimage]
# desktop entry generated from the deb template above via the linux config

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- allows us to load minecraft libraries -->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- allows us to set DYLD_FALLBACK_LIBRARY_PATH set by cargo which breaks minecraft -->
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<!-- allows us to request microphone permissions (for things like voice chat) -->
<key>com.apple.security.device.audio-input</key>
<true/>
<!-- allows us to request camera permissions (for things like voice chat) -->
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>

View file

@ -0,0 +1,12 @@
[Desktop Entry]
Name=OneClient
Comment=Next-generation open source Minecraft launcher
Exec={{exec}}
Icon={{icon}}
Type=Application
Terminal=false
StartupNotify=true
Categories=Game;ActionGame;AdventureGame;Simulation;
Keywords=mc;minecraft;game
StartupWMClass={{exec}}
Version=1.4

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -10,6 +10,7 @@ mod notifications;
pub mod platform;
mod routes;
pub mod theme;
pub mod updater;
mod ui;
pub(crate) mod utils;
mod view;

View file

@ -46,6 +46,8 @@ fn main() {
oneclient_core::logger::init()
}.expect("Failed to initialize logger");
oneclient_app::updater::spawn_update_check(settings.auto_update);
let (bridge, handle): (OneClientBridge, CoreBridgeHandle) = OneClientBridge::new();
handle.spawn_runtime();

View file

@ -0,0 +1,42 @@
//! In-app self-update via [`cargo-packager-updater`].
use cargo_packager_updater::{Config, check_update};
pub const UPDATER_PUBKEY: &str = "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDFGODk3MkMyMjg0MjFDMDUKUldRRkhFSW93bktKSHpkWjNEMXNzaDVINVpCTU8xSnhuK2RnV0dTZ2FkcFJWbG1zUkhGYTNjaUkK";
pub const UPDATER_ENDPOINT: &str =
"https://github.com/Polyfrost/OneLauncher/releases/latest/download/latest.json";
pub fn spawn_update_check(auto_install: bool) {
std::thread::Builder::new()
.name("oneclient-updater".into())
.spawn(move || {
if let Err(err) = run_check(auto_install) {
tracing::warn!("update check failed: {err}");
}
})
.ok();
}
fn run_check(auto_install: bool) -> anyhow::Result<()> {
let current = env!("CARGO_PKG_VERSION").parse()?;
let config = Config {
endpoints: vec![UPDATER_ENDPOINT.parse()?],
pubkey: UPDATER_PUBKEY.into(),
..Default::default()
};
match check_update(current, config)? {
Some(update) => {
tracing::info!("update available: {}", update.version);
if auto_install {
tracing::info!("downloading and installing update…");
update.download_and_install()?;
tracing::info!("update installed; restart to apply");
}
}
None => tracing::info!("no update available"),
}
Ok(())
}

View file

@ -25,6 +25,7 @@ pub struct ViewState {
pub struct LauncherSettings {
pub settings_version: u32,
pub log_debug: bool,
pub auto_update: bool,
pub enable_gamemode: bool,
pub discord_enabled: bool,
pub max_concurrent_requests: usize,
@ -55,6 +56,7 @@ impl Default for LauncherSettings {
Self {
settings_version: 1,
log_debug: false,
auto_update: true,
discord_enabled: true,
enable_gamemode: false,
max_concurrent_requests: 25,