Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions bin/omarchy-install-cursor
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@

set -euo pipefail

VERSION="2.6.20"
ARCH="$(uname -m)"
CURSOR_DIR="/opt/cursor"
CURSOR_APPIMAGE="$CURSOR_DIR/cursor.AppImage"
CURSOR_VERSION_FILE="$CURSOR_DIR/version"
CURSOR_RELEASE_API="https://www.cursor.com/api/download?platform=linux-arm64&releaseTrack=latest"
CURSOR_BIN="/usr/local/bin/cursor"
CURSOR_DESKTOP="/usr/share/applications/cursor.desktop"
CURSOR_URL_HANDLER_DESKTOP="/usr/share/applications/cursor-url-handler.desktop"
Expand Down Expand Up @@ -44,7 +45,7 @@ EOF
}

install_cursor_aarch64() {
local tmp_dir appimage_url wrapper_path desktop_template url_handler_template icon_size icon_source
local tmp_dir metadata version appimage_url wrapper_path desktop_template url_handler_template icon_size icon_source

if [[ -x $CURSOR_BIN && -f $CURSOR_APPIMAGE ]]; then
echo "Cursor is already installed at $CURSOR_APPIMAGE"
Expand All @@ -54,10 +55,12 @@ install_cursor_aarch64() {
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' RETURN

appimage_url="https://api2.cursor.sh/updates/download/golden/linux-arm64/cursor/$VERSION"
metadata=$(curl -fsSL "$CURSOR_RELEASE_API")
version=$(jq -er '.version' <<<"$metadata")
appimage_url=$(jq -er '.downloadUrl' <<<"$metadata")
wrapper_path="$tmp_dir/cursor"

echo "Downloading Cursor $VERSION for aarch64..."
echo "Downloading Cursor $version for aarch64..."
curl -L --fail --output "$tmp_dir/cursor.AppImage" "$appimage_url"
chmod +x "$tmp_dir/cursor.AppImage"

Expand All @@ -78,6 +81,7 @@ EOF
sudo mkdir -p "$CURSOR_DIR"
sudo install -m 755 "$tmp_dir/cursor.AppImage" "$CURSOR_APPIMAGE"
sudo install -m 755 "$wrapper_path" "$CURSOR_BIN"
printf '%s\n' "$version" | sudo tee "$CURSOR_VERSION_FILE" >/dev/null

desktop_template="$tmp_dir/squashfs-root/usr/share/applications/cursor.desktop"
url_handler_template="$tmp_dir/squashfs-root/usr/share/applications/cursor-url-handler.desktop"
Expand Down
1 change: 1 addition & 0 deletions bin/omarchy-update
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ if [[ ${1:-} == "-y" ]] || omarchy-update-confirm; then
omarchy-hook post-update
omarchy-update-aur-pkgs
omarchy-update-mise
omarchy-update-cursor
omarchy-update-orphan-pkgs

omarchy-update-analyze-logs
Expand Down
36 changes: 36 additions & 0 deletions bin/omarchy-update-cursor
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/bin/bash

# omarchy:summary=Update the Cursor AppImage on Apple Silicon
# omarchy:requires-sudo=true

set -euo pipefail

CURSOR_DIR="${OMARCHY_CURSOR_DIR:-/opt/cursor}"
CURSOR_APPIMAGE="$CURSOR_DIR/cursor.AppImage"
CURSOR_VERSION_FILE="$CURSOR_DIR/version"
CURSOR_RELEASE_API="https://www.cursor.com/api/download?platform=linux-arm64&releaseTrack=latest"

# x86_64 installs Cursor as cursor-bin, so pacman/AUR already moved it.
# Machines that never chose Cursor have no AppImage to refresh.
if [[ $(uname -m) == "aarch64" && -f $CURSOR_APPIMAGE ]]; then
echo -e "\e[32m\nUpdate Cursor\e[0m"

metadata=$(curl -fsSL "$CURSOR_RELEASE_API")
latest_version=$(jq -er '.version' <<<"$metadata")
download_url=$(jq -er '.downloadUrl' <<<"$metadata")

if [[ -f $CURSOR_VERSION_FILE && $(<"$CURSOR_VERSION_FILE") == "$latest_version" ]]; then
echo "Cursor $latest_version already installed"
else
tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

echo "Downloading Cursor $latest_version..."
curl -L --fail --output "$tmp_dir/cursor.AppImage" "$download_url"
chmod +x "$tmp_dir/cursor.AppImage"

sudo mkdir -p "$CURSOR_DIR"
sudo install -m 755 "$tmp_dir/cursor.AppImage" "$CURSOR_APPIMAGE"
printf '%s\n' "$latest_version" | sudo tee "$CURSOR_VERSION_FILE" >/dev/null
fi
fi
10 changes: 8 additions & 2 deletions docs/update-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ scripts.
| `omarchy-update-available` | Update checker for shell widget and post-update refresh. | **Keep.** Could eventually be renamed `omarchy-update-check`, but current name matches widget semantics. |
| `omarchy-update-aur-pkgs` | Updates AUR packages with `yay -Sua` if foreign packages exist and AUR is reachable. | **Question.** Omarchy is package-backed now, but users may still install AUR packages. Keep for now. |
| `omarchy-update-mise` | Runs `MISE_MINIMUM_RELEASE_AGE=0 mise up` for mise-managed tools — the override of mise's release-age cooldown is the point. | **Keep.** Mise-managed tools are intentionally part of the blessed update path. |
| `omarchy-update-cursor` | Replaces `/opt/cursor/cursor.AppImage` from Cursor's linux-arm64 latest API when the installed version is behind. No-ops on x86_64 and when the AppImage is absent. | **Keep.** The aarch64 installer disables in-app updates (`update.mode: none`), so Omarchy has to own the AppImage. |
| `omarchy-update-orphan-pkgs` | Lists orphans and prompts before removal; noninteractive mode never removes. | **Keep for now.** Safe because it is prompt-only. |
| `omarchy-update-analyze-logs` | Scans `/tmp/omarchy-update.log` for known failure patterns, currently initramfs generation. | **Keep/expand.** Useful safety net; should grow only for high-signal checks. |
| `omarchy-update-restart` | Prompts for reboot after kernel/Hyprland updates, restarts components with `restart-*-required` markers, and always restarts the shell. | **Keep.** Important final step; may eventually include service-restart checks. |
Expand All @@ -308,10 +309,15 @@ scripts.
4. **Mise remains in the blessed update path**
- `omarchy-update-mise` intentionally runs as part of `omarchy update`.

5. **Orphan cleanup stays in the update path for now**
5. **ARM Cursor AppImage is part of the blessed update path**
- `omarchy-update-cursor` runs after mise.
- It no-ops on x86_64, where Cursor is `cursor-bin`, and when `/opt/cursor/cursor.AppImage` is not installed.
- In-app Cursor updates stay disabled; Omarchy replaces the AppImage.

6. **Orphan cleanup stays in the update path for now**
- It is prompt-only and never removes packages noninteractively.

6. **Direct pacman user follow-up is based on actual migration state**
7. **Direct pacman user follow-up is based on actual migration state**
- Direct `sudo pacman -Syu` no longer uses a fake user-update marker.
- User notifications are shown only when `omarchy-migrate --pending` finds
missing per-user migration state.
Expand Down
133 changes: 133 additions & 0 deletions test/shell.d/update-cursor-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/bin/bash

set -euo pipefail

source "$(dirname "$0")/base-test.sh"

require_command jq

test_tmp=$(mktemp -d)
trap 'rm -rf "$test_tmp"' EXIT

stub_bin="$test_tmp/bin"
cursor_dir="$test_tmp/cursor"
curl_log="$test_tmp/curl.log"
latest_version="3.20.17"
download_url="https://downloads.cursor.com/production/deadbeef/linux/arm64/Cursor-3.20.17-aarch64.AppImage"
mkdir -p "$stub_bin" "$cursor_dir"

cat >"$stub_bin/uname" <<'SH'
#!/bin/bash
if [[ ${1:-} == "-m" ]]; then
printf '%s\n' "${TEST_UNAME_M:-aarch64}"
exit 0
fi
exec /usr/bin/uname "$@"
SH
chmod +x "$stub_bin/uname"

cat >"$stub_bin/curl" <<'SH'
#!/bin/bash
printf '%s\n' "$*" >>"$CURL_LOG"

output=""
url=""
while [[ $# -gt 0 ]]; do
case "$1" in
-o | --output)
output=$2
shift 2
;;
http*)
url=$1
shift
;;
*)
shift
;;
esac
done

if [[ $url == *"/api/download"* ]]; then
json=$(printf '{"version":"%s","downloadUrl":"%s"}\n' "$CURSOR_LATEST" "$CURSOR_DOWNLOAD_URL")
if [[ -n $output ]]; then
printf '%s' "$json" >"$output"
else
printf '%s' "$json"
fi
exit 0
fi

if [[ $url == "$CURSOR_DOWNLOAD_URL" ]]; then
[[ -n $output ]] || exit 1
printf 'appimage-bytes\n' >"$output"
exit 0
fi

echo "unexpected curl url: ${url:-none}" >&2
exit 1
SH
chmod +x "$stub_bin/curl"

cat >"$stub_bin/sudo" <<'SH'
#!/bin/bash
exec "$@"
SH
chmod +x "$stub_bin/sudo"

run_update() {
: >"$curl_log"
CURL_LOG="$curl_log" \
CURSOR_LATEST="$latest_version" \
CURSOR_DOWNLOAD_URL="$download_url" \
TEST_UNAME_M="${TEST_UNAME_M:-aarch64}" \
OMARCHY_CURSOR_DIR="$cursor_dir" \
PATH="$stub_bin:$PATH" \
"$ROOT/bin/omarchy-update-cursor" >"$test_tmp/out" 2>"$test_tmp/err"
}

run_update
[[ ! -s $curl_log ]] || fail "aarch64 without an AppImage does not contact Cursor" "$(cat "$curl_log")"
pass "aarch64 without an AppImage is a no-op"

printf 'old-bytes\n' >"$cursor_dir/cursor.AppImage"
TEST_UNAME_M=x86_64 run_update
[[ ! -s $curl_log ]] || fail "x86_64 does not contact Cursor" "$(cat "$curl_log")"
pass "x86_64 leaves Cursor to cursor-bin"

printf '%s\n' "$latest_version" >"$cursor_dir/version"
run_update
grep -F '/api/download' "$curl_log" >/dev/null ||
fail "a current AppImage still checks the latest version" "$(cat "$curl_log")"
if grep -Fq -- "$download_url" "$curl_log"; then
fail "a current AppImage is downloaded again" "$(cat "$curl_log")"
fi
[[ $(<"$cursor_dir/cursor.AppImage") == "old-bytes" ]] ||
fail "a current AppImage is left in place"
pass "matching version skips the download"

printf '2.6.20\n' >"$cursor_dir/version"
run_update
[[ $(<"$cursor_dir/cursor.AppImage") == "appimage-bytes" ]] ||
fail "an older AppImage is replaced" "$(cat "$cursor_dir/cursor.AppImage")"
[[ $(<"$cursor_dir/version") == "$latest_version" ]] ||
fail "the installed version file is updated" "$(cat "$cursor_dir/version")"
pass "an older AppImage is replaced"

printf 'old-bytes\n' >"$cursor_dir/cursor.AppImage"
rm -f "$cursor_dir/version"
run_update
[[ $(<"$cursor_dir/cursor.AppImage") == "appimage-bytes" ]] ||
fail "an AppImage without a version file is replaced"
[[ $(<"$cursor_dir/version") == "$latest_version" ]] ||
fail "a missing version file is written after the download"
pass "a missing version file is treated as outdated"

if grep -Fq 'VERSION="2.6.20"' "$ROOT/bin/omarchy-install-cursor"; then
fail "the aarch64 installer still pins Cursor 2.6.20"
fi
grep -Fq 'platform=linux-arm64&releaseTrack=latest' "$ROOT/bin/omarchy-install-cursor" ||
fail "the aarch64 installer does not fetch the current Cursor release"
grep -Fq '$CURSOR_VERSION_FILE' "$ROOT/bin/omarchy-install-cursor" ||
fail "the aarch64 installer does not record the installed version"
pass "the aarch64 installer fetches current Cursor and records its version"
1 change: 1 addition & 0 deletions test/shell.d/update-disk-space-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ for command in \
omarchy-migrate \
omarchy-update-aur-pkgs \
omarchy-update-mise \
omarchy-update-cursor \
omarchy-update-orphan-pkgs \
omarchy-hook \
omarchy-update-analyze-logs \
Expand Down
1 change: 1 addition & 0 deletions test/shell.d/update-lock-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ for command in \
omarchy-migrate \
omarchy-update-aur-pkgs \
omarchy-update-mise \
omarchy-update-cursor \
omarchy-update-orphan-pkgs \
omarchy-hook \
omarchy-update-analyze-logs \
Expand Down
2 changes: 2 additions & 0 deletions test/shell.d/update-sequence-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ steps=(
omarchy-hook
omarchy-update-aur-pkgs
omarchy-update-mise
omarchy-update-cursor
omarchy-update-orphan-pkgs
omarchy-update-analyze-logs
omarchy-update-status
Expand Down Expand Up @@ -73,6 +74,7 @@ expected_steps() {
omarchy-hook \
omarchy-update-aur-pkgs \
omarchy-update-mise \
omarchy-update-cursor \
omarchy-update-orphan-pkgs \
omarchy-update-analyze-logs \
omarchy-update-status \
Expand Down
Loading