mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
Pin installer and updater downloads to the builds tip commit.
raw.githubusercontent.com caches branch-named files, so the installer was serving a stale zip after CI rewrote builds. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
0d40e12788
commit
7fabe1846e
5 changed files with 151 additions and 64 deletions
35
README.md
35
README.md
|
|
@ -19,16 +19,18 @@ zatím zamčené placeholdery. Aplikace se po instalaci aktualizuje sama.
|
||||||
|
|
||||||
Žádné releasy ani čísla verzí nejsou. CI po každém pushi na `main` uloží
|
Žádné releasy ani čísla verzí nejsou. CI po každém pushi na `main` uloží
|
||||||
balíčky do větve [`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds).
|
balíčky do větve [`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds).
|
||||||
Instalátor samotnou aplikaci neobsahuje — při spuštění si stáhne aktuální
|
Instalátor samotnou aplikaci neobsahuje — při spuštění si dohledá aktuální
|
||||||
build, takže soubor nikdy nezestárne.
|
commit větve `builds` a stáhne ten soubor, ne kešovanou kopii podle názvu
|
||||||
|
větve.
|
||||||
|
|
||||||
**Nejjednodušší je instalátor pro vaši platformu:**
|
**Nejjednodušší je instalátor pro vaši platformu** (soubory z `main`, ať
|
||||||
|
nedostanete starý skript z keše větve `builds`):
|
||||||
|
|
||||||
| Platforma | Soubor | Jak spustit |
|
| Platforma | Soubor | Jak spustit |
|
||||||
| --------- | ------ | ----------- |
|
| --------- | ------ | ----------- |
|
||||||
| macOS (Apple Silicon) | [maturita-installer-macos.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-macos.zip) | Rozbalit, pravý klik → **Otevřít** |
|
| macOS (Apple Silicon) | [maturita-installer-macos.command](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-macos.command) | Stáhnout, v Terminálu `chmod +x` a pak pravý klik → **Otevřít** |
|
||||||
| Windows (x64) | [maturita-installer-windows.cmd](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-windows.cmd) | Dvojklik; u SmartScreenu **Další informace → Přesto spustit** |
|
| Windows (x64) | [maturita-installer-windows.cmd](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-windows.cmd) | Dvojklik; u SmartScreenu **Další informace → Přesto spustit** |
|
||||||
| Linux (x86_64) | [maturita-installer-linux.sh](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-linux.sh) | `sh maturita-installer-linux.sh` |
|
| Linux (x86_64) | [maturita-installer-linux.sh](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-linux.sh) | `sh maturita-installer-linux.sh` |
|
||||||
|
|
||||||
Kam se to nainstaluje: `/Applications` nebo `~/Applications` (macOS),
|
Kam se to nainstaluje: `/Applications` nebo `~/Applications` (macOS),
|
||||||
`%LOCALAPPDATA%\Programs\Maturita` (Windows), `~/Applications` (Linux).
|
`%LOCALAPPDATA%\Programs\Maturita` (Windows), `~/Applications` (Linux).
|
||||||
|
|
@ -41,7 +43,9 @@ v **Nastavení → Aktualizace**.
|
||||||
|
|
||||||
#### Bez instalátoru
|
#### Bez instalátoru
|
||||||
|
|
||||||
Na stejné větvi jsou i hotové balíčky:
|
Na větvi [`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds)
|
||||||
|
jsou i hotové balíčky. Odkazy podle názvu větve umí CDN kešovat, proto je
|
||||||
|
spolehlivější instalátor výše, nebo stažení přímo z té stránky větve:
|
||||||
|
|
||||||
- [maturita-macos-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-arm64.zip)
|
- [maturita-macos-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-arm64.zip)
|
||||||
- [maturita-windows-x64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-windows-x64.zip)
|
- [maturita-windows-x64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-windows-x64.zip)
|
||||||
|
|
@ -152,15 +156,17 @@ placeholders. After install the app updates itself.
|
||||||
There are no releases and no version numbers. CI writes packages to the
|
There are no releases and no version numbers. CI writes packages to the
|
||||||
[`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds) branch
|
[`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds) branch
|
||||||
whenever `main` changes. The installer carries no app of its own — it fetches
|
whenever `main` changes. The installer carries no app of its own — it fetches
|
||||||
the current build when you run it, so the file never goes stale.
|
the current `builds` commit when you run it, not a CDN-cached copy of the
|
||||||
|
branch-named file.
|
||||||
|
|
||||||
**Start with the installer for your platform:**
|
**Start with the installer for your platform** (files from `main`, so you
|
||||||
|
do not get a stale script cached from the `builds` branch):
|
||||||
|
|
||||||
| Platform | File | How to run |
|
| Platform | File | How to run |
|
||||||
| -------- | ---- | ---------- |
|
| -------- | ---- | ---------- |
|
||||||
| macOS (Apple Silicon) | [maturita-installer-macos.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-macos.zip) | Unzip, then right-click → **Open** |
|
| macOS (Apple Silicon) | [maturita-installer-macos.command](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-macos.command) | Download, `chmod +x` in Terminal, then right-click → **Open** |
|
||||||
| Windows (x64) | [maturita-installer-windows.cmd](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-windows.cmd) | Double-click; if SmartScreen appears, **More info → Run anyway** |
|
| Windows (x64) | [maturita-installer-windows.cmd](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-windows.cmd) | Double-click; if SmartScreen appears, **More info → Run anyway** |
|
||||||
| Linux (x86_64) | [maturita-installer-linux.sh](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-installer-linux.sh) | `sh maturita-installer-linux.sh` |
|
| Linux (x86_64) | [maturita-installer-linux.sh](https://github.com/pepaskopec-blip/maturita.c/raw/main/installers/maturita-installer-linux.sh) | `sh maturita-installer-linux.sh` |
|
||||||
|
|
||||||
Install locations: `/Applications` or `~/Applications` (macOS),
|
Install locations: `/Applications` or `~/Applications` (macOS),
|
||||||
`%LOCALAPPDATA%\Programs\Maturita` (Windows), `~/Applications` (Linux).
|
`%LOCALAPPDATA%\Programs\Maturita` (Windows), `~/Applications` (Linux).
|
||||||
|
|
@ -172,6 +178,11 @@ banner. Manual check: **Settings → Updates**.
|
||||||
|
|
||||||
#### Packages without the installer
|
#### Packages without the installer
|
||||||
|
|
||||||
|
The same files live on
|
||||||
|
[`builds`](https://github.com/pepaskopec-blip/maturita.c/tree/builds).
|
||||||
|
Branch-named raw URLs can be cached by the CDN, so prefer the installer
|
||||||
|
above, or download from that branch page:
|
||||||
|
|
||||||
- [maturita-macos-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-arm64.zip)
|
- [maturita-macos-arm64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-macos-arm64.zip)
|
||||||
- [maturita-windows-x64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-windows-x64.zip)
|
- [maturita-windows-x64.zip](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-windows-x64.zip)
|
||||||
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
- [maturita-linux-x86_64.AppImage](https://github.com/pepaskopec-blip/maturita.c/raw/builds/maturita-linux-x86_64.AppImage)
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# maturita.c installer for Linux.
|
# maturita.c installer for Linux.
|
||||||
#
|
#
|
||||||
# Carries no application of its own: it downloads the current build straight
|
# Carries no application of its own. Fastly caches branch-named URLs on
|
||||||
# from the repository's `builds` branch, which CI rewrites whenever main
|
# raw.githubusercontent.com, so this script looks up the tip of `builds`
|
||||||
# changes. This file therefore never goes stale.
|
# and downloads that commit — not a stale AppImage from the last five minutes.
|
||||||
#
|
#
|
||||||
# Run it with: sh maturita-installer-linux.sh
|
# Run it with: sh maturita-installer-linux.sh
|
||||||
# The app updates itself from then on.
|
# The app updates itself from then on.
|
||||||
|
|
@ -13,10 +13,7 @@ set -eu
|
||||||
REPO="pepaskopec-blip/maturita.c"
|
REPO="pepaskopec-blip/maturita.c"
|
||||||
BRANCH="builds"
|
BRANCH="builds"
|
||||||
ASSET="maturita-linux-x86_64.AppImage"
|
ASSET="maturita-linux-x86_64.AppImage"
|
||||||
URL="https://raw.githubusercontent.com/$REPO/$BRANCH/$ASSET"
|
|
||||||
|
|
||||||
# ~/Applications is where desktop environments look for AppImages, and the app
|
|
||||||
# keeps its progress/ directory next to its own file.
|
|
||||||
DEST_DIR="$HOME/Applications"
|
DEST_DIR="$HOME/Applications"
|
||||||
DEST="$DEST_DIR/maturita.AppImage"
|
DEST="$DEST_DIR/maturita.AppImage"
|
||||||
DESKTOP_DIR="$HOME/.local/share/applications"
|
DESKTOP_DIR="$HOME/.local/share/applications"
|
||||||
|
|
@ -25,6 +22,13 @@ ICON_DIR="$HOME/.local/share/icons/hicolor/512x512/apps"
|
||||||
say() { printf '%s\n' "$*"; }
|
say() { printf '%s\n' "$*"; }
|
||||||
die() { printf '\nError: %s\n' "$*" >&2; exit 1; }
|
die() { printf '\nError: %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
builds_sha() {
|
||||||
|
curl -fsSL --max-time 20 \
|
||||||
|
"https://github.com/$REPO/commits/$BRANCH.atom" |
|
||||||
|
sed -n 's/.*Commit\/\([0-9a-f]\{40\}\).*/\1/p' |
|
||||||
|
head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
say "Installing maturita.C"
|
say "Installing maturita.C"
|
||||||
say "---------------------"
|
say "---------------------"
|
||||||
|
|
||||||
|
|
@ -41,16 +45,19 @@ mkdir -p "$DEST_DIR"
|
||||||
tmp=$(mktemp -d)
|
tmp=$(mktemp -d)
|
||||||
trap 'rm -rf "$tmp"' EXIT INT TERM
|
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||||
|
|
||||||
say "Downloading the latest build..."
|
say "Looking up the latest build..."
|
||||||
curl -fL --progress-bar -o "$tmp/$ASSET" "$URL" ||
|
sha=$(builds_sha)
|
||||||
|
[ ${#sha} -eq 40 ] || die "could not find the latest build on GitHub."
|
||||||
|
URL="https://raw.githubusercontent.com/$REPO/$sha/$ASSET"
|
||||||
|
|
||||||
|
say "Downloading build $(printf '%.7s' "$sha")..."
|
||||||
|
curl -fL --progress-bar --max-time 1800 -o "$tmp/$ASSET" "$URL" ||
|
||||||
die "the download failed. Check your internet connection."
|
die "the download failed. Check your internet connection."
|
||||||
|
|
||||||
say "Installing to $DEST"
|
say "Installing to $DEST"
|
||||||
chmod +x "$tmp/$ASSET"
|
chmod +x "$tmp/$ASSET"
|
||||||
mv -f "$tmp/$ASSET" "$DEST" || die "could not write to $DEST_DIR."
|
mv -f "$tmp/$ASSET" "$DEST" || die "could not write to $DEST_DIR."
|
||||||
|
|
||||||
# A menu entry needs the icon as a separate file, which the AppImage can
|
|
||||||
# unpack without FUSE. Losing the icon is not worth failing the install over.
|
|
||||||
if (cd "$tmp" && "$DEST" --appimage-extract \
|
if (cd "$tmp" && "$DEST" --appimage-extract \
|
||||||
'usr/share/icons/hicolor/512x512/apps/maturita.png' >/dev/null 2>&1); then
|
'usr/share/icons/hicolor/512x512/apps/maturita.png' >/dev/null 2>&1); then
|
||||||
icon="$tmp/squashfs-root/usr/share/icons/hicolor/512x512/apps/maturita.png"
|
icon="$tmp/squashfs-root/usr/share/icons/hicolor/512x512/apps/maturita.png"
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# maturita.c installer for macOS.
|
# maturita.c installer for macOS.
|
||||||
#
|
#
|
||||||
# Carries no application of its own: it downloads the current build straight
|
# Carries no application of its own. Fastly caches branch-named URLs on
|
||||||
# from the repository's `builds` branch, which CI rewrites whenever main
|
# raw.githubusercontent.com, so this script looks up the tip of `builds`
|
||||||
# changes. This file therefore never goes stale.
|
# and downloads that commit — not a stale zip from the last five minutes.
|
||||||
#
|
#
|
||||||
# Double-click the file to run it. The app updates itself from then on.
|
# Double-click the file to run it. The app updates itself from then on.
|
||||||
|
|
||||||
|
|
@ -12,15 +12,21 @@ set -eu
|
||||||
REPO="pepaskopec-blip/maturita.c"
|
REPO="pepaskopec-blip/maturita.c"
|
||||||
BRANCH="builds"
|
BRANCH="builds"
|
||||||
ASSET="maturita-macos-arm64.zip"
|
ASSET="maturita-macos-arm64.zip"
|
||||||
URL="https://raw.githubusercontent.com/$REPO/$BRANCH/$ASSET"
|
|
||||||
|
|
||||||
say() { printf '%s\n' "$*"; }
|
say() { printf '%s\n' "$*"; }
|
||||||
die() { printf '\nError: %s\n' "$*" >&2; printf 'Press Return to close. '; read -r _; exit 1; }
|
die() { printf '\nError: %s\n' "$*" >&2; printf 'Press Return to close. '; read -r _; exit 1; }
|
||||||
|
|
||||||
|
# Atom feed of the builds branch: curl only, no GitHub API token.
|
||||||
|
builds_sha() {
|
||||||
|
curl -fsSL --max-time 20 \
|
||||||
|
"https://github.com/$REPO/commits/$BRANCH.atom" |
|
||||||
|
sed -n 's/.*Commit\/\([0-9a-f]\{40\}\).*/\1/p' |
|
||||||
|
head -n 1
|
||||||
|
}
|
||||||
|
|
||||||
say "Installing maturita.C"
|
say "Installing maturita.C"
|
||||||
say "---------------------"
|
say "---------------------"
|
||||||
|
|
||||||
# Only Apple Silicon builds are published; Rosetta cannot run an arm64 app.
|
|
||||||
arch=$(uname -m)
|
arch=$(uname -m)
|
||||||
if [ "$arch" != "arm64" ]; then
|
if [ "$arch" != "arm64" ]; then
|
||||||
die "this build is for Apple Silicon Macs, but this one reports '$arch'.
|
die "this build is for Apple Silicon Macs, but this one reports '$arch'.
|
||||||
|
|
@ -29,7 +35,6 @@ fi
|
||||||
|
|
||||||
command -v curl >/dev/null 2>&1 || die "curl was not found."
|
command -v curl >/dev/null 2>&1 || die "curl was not found."
|
||||||
|
|
||||||
# Prefer the shared folder, fall back to the user's own when it is locked down.
|
|
||||||
if [ -w /Applications ]; then
|
if [ -w /Applications ]; then
|
||||||
dest_dir="/Applications"
|
dest_dir="/Applications"
|
||||||
else
|
else
|
||||||
|
|
@ -40,13 +45,17 @@ fi
|
||||||
tmp=$(mktemp -d)
|
tmp=$(mktemp -d)
|
||||||
trap 'rm -rf "$tmp"' EXIT INT TERM
|
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||||
|
|
||||||
say "Downloading the latest build..."
|
say "Looking up the latest build..."
|
||||||
curl -fL --progress-bar -o "$tmp/$ASSET" "$URL" ||
|
sha=$(builds_sha)
|
||||||
|
[ ${#sha} -eq 40 ] || die "could not find the latest build on GitHub."
|
||||||
|
URL="https://raw.githubusercontent.com/$REPO/$sha/$ASSET"
|
||||||
|
|
||||||
|
say "Downloading build $(printf '%.7s' "$sha")..."
|
||||||
|
curl -fL --progress-bar --max-time 1800 -o "$tmp/$ASSET" "$URL" ||
|
||||||
die "the download failed. Check your internet connection."
|
die "the download failed. Check your internet connection."
|
||||||
|
|
||||||
say "Unpacking..."
|
say "Unpacking..."
|
||||||
mkdir -p "$tmp/new"
|
mkdir -p "$tmp/new"
|
||||||
# ditto keeps bundle symlinks and permissions intact, unlike plain unzip.
|
|
||||||
ditto -x -k "$tmp/$ASSET" "$tmp/new" || die "the archive could not be unpacked."
|
ditto -x -k "$tmp/$ASSET" "$tmp/new" || die "the archive could not be unpacked."
|
||||||
|
|
||||||
app=$(find "$tmp/new" -maxdepth 1 -name '*.app' -print -quit)
|
app=$(find "$tmp/new" -maxdepth 1 -name '*.app' -print -quit)
|
||||||
|
|
@ -61,13 +70,10 @@ fi
|
||||||
if mv "$app" "$dest_dir/$name"; then
|
if mv "$app" "$dest_dir/$name"; then
|
||||||
rm -rf "$dest_dir/$name.old"
|
rm -rf "$dest_dir/$name.old"
|
||||||
else
|
else
|
||||||
# Put the previous copy back rather than leaving nothing installed.
|
|
||||||
[ -e "$dest_dir/$name.old" ] && mv "$dest_dir/$name.old" "$dest_dir/$name"
|
[ -e "$dest_dir/$name.old" ] && mv "$dest_dir/$name.old" "$dest_dir/$name"
|
||||||
die "could not write to $dest_dir."
|
die "could not write to $dest_dir."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# The download carries a quarantine flag, and the app is not signed, so
|
|
||||||
# without this macOS refuses to open it.
|
|
||||||
xattr -cr "$dest_dir/$name" 2>/dev/null || true
|
xattr -cr "$dest_dir/$name" 2>/dev/null || true
|
||||||
|
|
||||||
say ""
|
say ""
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,21 @@
|
||||||
@echo off
|
@echo off
|
||||||
rem maturita.c installer for Windows.
|
rem maturita.c installer for Windows.
|
||||||
rem
|
rem
|
||||||
rem Carries no application of its own: it downloads the current build straight
|
rem Carries no application of its own. Fastly caches branch-named URLs on
|
||||||
rem from the repository's `builds` branch, which CI rewrites whenever main
|
rem raw.githubusercontent.com, so this script looks up the tip of `builds`
|
||||||
rem changes. This file therefore never goes stale.
|
rem and downloads that commit — not a stale zip from the last five minutes.
|
||||||
rem
|
rem
|
||||||
rem Double-click the file to run it. The app updates itself from then on.
|
rem Double-click the file to run it. The app updates itself from then on.
|
||||||
|
|
||||||
setlocal
|
setlocal EnableDelayedExpansion
|
||||||
set "REPO=pepaskopec-blip/maturita.c"
|
set "REPO=pepaskopec-blip/maturita.c"
|
||||||
set "BRANCH=builds"
|
set "BRANCH=builds"
|
||||||
set "ASSET=maturita-windows-x64.zip"
|
set "ASSET=maturita-windows-x64.zip"
|
||||||
set "URL=https://raw.githubusercontent.com/%REPO%/%BRANCH%/%ASSET%"
|
|
||||||
set "DEST=%LOCALAPPDATA%\Programs\Maturita"
|
set "DEST=%LOCALAPPDATA%\Programs\Maturita"
|
||||||
|
|
||||||
echo Installing maturita.C
|
echo Installing maturita.C
|
||||||
echo ---------------------
|
echo ---------------------
|
||||||
|
|
||||||
rem curl and tar both ship with Windows 10 1803 and later.
|
|
||||||
where curl >nul 2>&1 || goto :no_tools
|
where curl >nul 2>&1 || goto :no_tools
|
||||||
where tar >nul 2>&1 || goto :no_tools
|
where tar >nul 2>&1 || goto :no_tools
|
||||||
|
|
||||||
|
|
@ -25,17 +23,20 @@ set "TMP_DIR=%TEMP%\maturita-installer"
|
||||||
rmdir /s /q "%TMP_DIR%" 2>nul
|
rmdir /s /q "%TMP_DIR%" 2>nul
|
||||||
mkdir "%TMP_DIR%" || goto :fail_tmp
|
mkdir "%TMP_DIR%" || goto :fail_tmp
|
||||||
|
|
||||||
echo Downloading the latest build...
|
echo Looking up the latest build...
|
||||||
curl -fL --progress-bar -o "%TMP_DIR%\%ASSET%" "%URL%" || goto :fail_download
|
set "SHA="
|
||||||
|
for /f "usebackq delims=" %%S in (`powershell -NoProfile -ExecutionPolicy Bypass -Command "$r = & curl.exe -fsSL --max-time 20 'https://github.com/%REPO%/commits/%BRANCH%.atom'; if ($r -match 'Commit/([0-9a-f]{40})') { $Matches[1] }"`) do set "SHA=%%S"
|
||||||
|
if not defined SHA goto :fail_download
|
||||||
|
set "URL=https://raw.githubusercontent.com/%REPO%/%SHA%/%ASSET%"
|
||||||
|
|
||||||
|
echo Downloading build %SHA:~0,7%...
|
||||||
|
curl -fL --progress-bar --max-time 1800 -o "%TMP_DIR%\%ASSET%" "%URL%" || goto :fail_download
|
||||||
|
|
||||||
echo Installing to %DEST%
|
echo Installing to %DEST%
|
||||||
if not exist "%DEST%" mkdir "%DEST%" || goto :fail_dest
|
if not exist "%DEST%" mkdir "%DEST%" || goto :fail_dest
|
||||||
|
|
||||||
rem Extracted over any existing install rather than replacing the folder, so
|
|
||||||
rem the progress files kept inside it survive an upgrade.
|
|
||||||
tar -xf "%TMP_DIR%\%ASSET%" -C "%DEST%" || goto :fail_extract
|
tar -xf "%TMP_DIR%\%ASSET%" -C "%DEST%" || goto :fail_extract
|
||||||
|
|
||||||
rem Start menu entry, so the app can be found without digging for the folder.
|
|
||||||
set "LNK=%APPDATA%\Microsoft\Windows\Start Menu\Programs\maturita.C.lnk"
|
set "LNK=%APPDATA%\Microsoft\Windows\Start Menu\Programs\maturita.C.lnk"
|
||||||
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
powershell -NoProfile -ExecutionPolicy Bypass -Command ^
|
||||||
"$s=(New-Object -ComObject WScript.Shell).CreateShortcut('%LNK%');" ^
|
"$s=(New-Object -ComObject WScript.Shell).CreateShortcut('%LNK%');" ^
|
||||||
|
|
|
||||||
100
src/update.c
100
src/update.c
|
|
@ -1,5 +1,7 @@
|
||||||
#include "maturita.h"
|
#include "maturita.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#define update_getpid() ((long)_getpid())
|
#define update_getpid() ((long)_getpid())
|
||||||
|
|
@ -23,9 +25,14 @@
|
||||||
* the repository, which CI rewrites on every change to main, so an update is
|
* the repository, which CI rewrites on every change to main, so an update is
|
||||||
* simply whatever that branch currently holds. The branch also carries a
|
* simply whatever that branch currently holds. The branch also carries a
|
||||||
* VERSION file naming the commit its packages were built from; comparing that
|
* VERSION file naming the commit its packages were built from; comparing that
|
||||||
* against the commit baked into this binary is the whole check. Serving it
|
* against the commit baked into this binary is the whole check.
|
||||||
* from raw.githubusercontent.com avoids the REST API and its unauthenticated
|
*
|
||||||
* rate limit, and needs no JSON parser.
|
* raw.githubusercontent.com is cached by Fastly by branch name, so a URL that
|
||||||
|
* says `/builds/VERSION` can keep serving yesterday's file after CI rewrote
|
||||||
|
* the branch. The check therefore reads the branch tip from the Atom feed
|
||||||
|
* (plain curl, no API token) and then fetches VERSION and the package from
|
||||||
|
* that commit SHA. Serving files from raw.githubusercontent.com still avoids
|
||||||
|
* the REST API and its unauthenticated rate limit, and needs no JSON parser.
|
||||||
*
|
*
|
||||||
* An application cannot reliably replace its own files while it is running, so
|
* An application cannot reliably replace its own files while it is running, so
|
||||||
* all three platforms follow the same shape: unpack the new version into a
|
* all three platforms follow the same shape: unpack the new version into a
|
||||||
|
|
@ -34,9 +41,8 @@
|
||||||
* script detached and quit. Staging beside the install keeps the final move on
|
* script detached and quit. Staging beside the install keeps the final move on
|
||||||
* one filesystem and proves up front that the location is writable. */
|
* one filesystem and proves up front that the location is writable. */
|
||||||
|
|
||||||
#define UPDATE_BASE_URL "https://raw.githubusercontent.com/" UPDATE_REPO \
|
#define UPDATE_ATOM_URL "https://github.com/" UPDATE_REPO \
|
||||||
"/" UPDATE_BRANCH "/"
|
"/commits/" UPDATE_BRANCH ".atom"
|
||||||
#define UPDATE_VERSION_URL UPDATE_BASE_URL "VERSION"
|
|
||||||
#define UPDATE_STAGING_DIR ".maturita-update"
|
#define UPDATE_STAGING_DIR ".maturita-update"
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
@ -68,6 +74,7 @@ typedef enum {
|
||||||
static struct {
|
static struct {
|
||||||
UpdateState state;
|
UpdateState state;
|
||||||
gboolean interactive; /* user asked, so also report "up to date" */
|
gboolean interactive; /* user asked, so also report "up to date" */
|
||||||
|
char *tip; /* current commit of the builds branch itself */
|
||||||
char *latest; /* commit the published packages were built from */
|
char *latest; /* commit the published packages were built from */
|
||||||
char *staging; /* scratch directory beside the install */
|
char *staging; /* scratch directory beside the install */
|
||||||
char *archive; /* asset downloaded into the staging directory */
|
char *archive; /* asset downloaded into the staging directory */
|
||||||
|
|
@ -114,6 +121,35 @@ static char *parse_commit(const char *text) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* First Grit::Commit/<40-hex> in a GitHub Atom feed is the branch tip. */
|
||||||
|
static char *parse_atom_commit(const char *text) {
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
if (!text)
|
||||||
|
return NULL;
|
||||||
|
p = strstr(text, "Commit/");
|
||||||
|
while (p) {
|
||||||
|
const char *s = p + 7;
|
||||||
|
size_t n = 0;
|
||||||
|
|
||||||
|
while (g_ascii_isxdigit(s[n]))
|
||||||
|
n++;
|
||||||
|
if (n == 40)
|
||||||
|
return g_strndup(s, 40);
|
||||||
|
p = strstr(s, "Commit/");
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pin raw.githubusercontent.com to a commit so Fastly cannot serve a stale
|
||||||
|
* branch-named file. Falls back to the branch name if the tip is unknown. */
|
||||||
|
static char *builds_raw_url(const char *name) {
|
||||||
|
const char *ref = (up.tip && up.tip[0]) ? up.tip : UPDATE_BRANCH;
|
||||||
|
|
||||||
|
return g_strdup_printf("https://raw.githubusercontent.com/%s/%s/%s",
|
||||||
|
UPDATE_REPO, ref, name);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean same_commit(const char *a, const char *b) {
|
static gboolean same_commit(const char *a, const char *b) {
|
||||||
char *ca = parse_commit(a);
|
char *ca = parse_commit(a);
|
||||||
char *cb = parse_commit(b);
|
char *cb = parse_commit(b);
|
||||||
|
|
@ -614,7 +650,7 @@ static void size_probe_done(GObject *src, GAsyncResult *res, gpointer data) {
|
||||||
if (up.state != UPDATE_DOWNLOADING)
|
if (up.state != UPDATE_DOWNLOADING)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
url = g_strconcat(UPDATE_BASE_URL, asset_name(), NULL);
|
url = builds_raw_url(asset_name());
|
||||||
argv[0] = "curl";
|
argv[0] = "curl";
|
||||||
argv[1] = "-fsSL";
|
argv[1] = "-fsSL";
|
||||||
argv[2] = "--max-time";
|
argv[2] = "--max-time";
|
||||||
|
|
@ -663,7 +699,7 @@ static void update_start_download(void) {
|
||||||
update_set_state(UPDATE_DOWNLOADING);
|
update_set_state(UPDATE_DOWNLOADING);
|
||||||
|
|
||||||
/* HEAD the asset with its headers on stdout, to size the progress bar. */
|
/* HEAD the asset with its headers on stdout, to size the progress bar. */
|
||||||
url = g_strconcat(UPDATE_BASE_URL, asset_name(), NULL);
|
url = builds_raw_url(asset_name());
|
||||||
argv[0] = "curl";
|
argv[0] = "curl";
|
||||||
argv[1] = "-fsSLI";
|
argv[1] = "-fsSLI";
|
||||||
argv[2] = "--max-time";
|
argv[2] = "--max-time";
|
||||||
|
|
@ -729,9 +765,41 @@ static void check_done(GObject *src, GAsyncResult *res, gpointer data) {
|
||||||
update_set_state(UPDATE_IDLE);
|
update_set_state(UPDATE_IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_check_async(gboolean interactive) {
|
static void tip_done(GObject *src, GAsyncResult *res, gpointer data) {
|
||||||
const char *argv[8];
|
GSubprocess *proc = G_SUBPROCESS(src);
|
||||||
|
char *out = NULL;
|
||||||
|
gboolean ok;
|
||||||
char *url;
|
char *url;
|
||||||
|
const char *argv[6];
|
||||||
|
|
||||||
|
(void)data;
|
||||||
|
g_subprocess_communicate_utf8_finish(proc, res, &out, NULL, NULL);
|
||||||
|
ok = g_subprocess_get_successful(proc);
|
||||||
|
g_object_unref(proc);
|
||||||
|
|
||||||
|
g_free(up.tip);
|
||||||
|
up.tip = (ok && out) ? parse_atom_commit(out) : NULL;
|
||||||
|
g_free(out);
|
||||||
|
if (!up.tip) {
|
||||||
|
check_giveup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
url = builds_raw_url("VERSION");
|
||||||
|
argv[0] = "curl";
|
||||||
|
argv[1] = "-fsSL";
|
||||||
|
argv[2] = "--max-time";
|
||||||
|
argv[3] = "20";
|
||||||
|
argv[4] = url;
|
||||||
|
argv[5] = NULL;
|
||||||
|
|
||||||
|
if (!curl_async(argv, check_done))
|
||||||
|
check_giveup();
|
||||||
|
g_free(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_check_async(gboolean interactive) {
|
||||||
|
const char *argv[6];
|
||||||
|
|
||||||
if (up.state == UPDATE_CHECKING || up.state == UPDATE_DOWNLOADING ||
|
if (up.state == UPDATE_CHECKING || up.state == UPDATE_DOWNLOADING ||
|
||||||
up.state == UPDATE_STAGED)
|
up.state == UPDATE_STAGED)
|
||||||
|
|
@ -748,25 +816,19 @@ void update_check_async(gboolean interactive) {
|
||||||
|
|
||||||
update_set_state(UPDATE_CHECKING);
|
update_set_state(UPDATE_CHECKING);
|
||||||
|
|
||||||
/* raw.githubusercontent.com caches VERSION; a unique query bypasses it. */
|
|
||||||
url = g_strdup_printf("%s?t=%ld", UPDATE_VERSION_URL,
|
|
||||||
(long)g_get_real_time());
|
|
||||||
argv[0] = "curl";
|
argv[0] = "curl";
|
||||||
argv[1] = "-fsSL";
|
argv[1] = "-fsSL";
|
||||||
argv[2] = "--max-time";
|
argv[2] = "--max-time";
|
||||||
argv[3] = "20";
|
argv[3] = "20";
|
||||||
argv[4] = "-H";
|
argv[4] = UPDATE_ATOM_URL;
|
||||||
argv[5] = "Cache-Control: no-cache";
|
argv[5] = NULL;
|
||||||
argv[6] = url;
|
|
||||||
argv[7] = NULL;
|
|
||||||
|
|
||||||
if (!curl_async(argv, check_done)) {
|
if (!curl_async(argv, tip_done)) {
|
||||||
if (interactive)
|
if (interactive)
|
||||||
update_fail(tr("update_err_curl"));
|
update_fail(tr("update_err_curl"));
|
||||||
else
|
else
|
||||||
update_set_state(UPDATE_IDLE);
|
update_set_state(UPDATE_IDLE);
|
||||||
}
|
}
|
||||||
g_free(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------ */
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue