diff --git a/pkgbuilds/flea/.omarchy/package.json b/pkgbuilds/flea/.omarchy/package.json new file mode 100644 index 0000000..bbe9ae1 --- /dev/null +++ b/pkgbuilds/flea/.omarchy/package.json @@ -0,0 +1,5 @@ +{ + "source": "local", + "release_ring": "fast", + "min_release_age": "24h" +} diff --git a/pkgbuilds/flea/.omarchy/upstream.sh b/pkgbuilds/flea/.omarchy/upstream.sh new file mode 100755 index 0000000..5f512b2 --- /dev/null +++ b/pkgbuilds/flea/.omarchy/upstream.sh @@ -0,0 +1,100 @@ +#!/bin/bash +# Flea publishes signed source tags but no release artifacts or checksum manifest. +# Check the small GitHub source tarball only when a newer stable release exists, +# validate its expected root, then report its hash alongside the fixed hashes of +# the four upstream security patches carried until a release includes them. +set -euo pipefail + +REPO='thisisgm/flea' +RELEASES_URL="https://api.github.com/repos/$REPO/releases?per_page=100" +PATCH_2BD7CC2='c610a9f44294b67341940203943c9c567003b65cc436d6013cd36754379183d8' +PATCH_27D19CA='e457ef17e26f70057b1184eeb938fccc5906f48a7c77f1df573d55d13a845425' +PATCH_23290CE='f8381456a3b5f39d341cc6610bf27beb8354892b17a1c8c6d7882db4e40824c3' +PATCH_B4B7EE4='46bdbe43f135a052c893b1987f8b0928736445616a84b0c65d2181ee74b21b25' + +current=$(awk -F= '/^pkgver=/ { print $2; exit }' PKGBUILD) +releases=$(curl -fsSL "$RELEASES_URL") +now=$(date +%s) +min_age=${MIN_RELEASE_AGE_SECONDS:-0} + +candidates=0 +best_version='' +best_tag='' +best_published_at='' + +while IFS=$'\t' read -r tag published_at; do + if [[ ! $tag =~ ^v([0-9]+\.[0-9]+\.[0-9]+)$ ]]; then + printf '%s has an unusable stable release tag: %s\n' "$REPO" "${tag:-}" >&2 + exit 1 + fi + version=${BASH_REMATCH[1]} + + if [[ -z $published_at ]] || ! published_epoch=$(date --date="$published_at" +%s 2>/dev/null); then + printf '%s release %s has an invalid publication time\n' "$REPO" "$tag" >&2 + exit 1 + fi + candidates=$((candidates + 1)) + + if (( now - published_epoch < min_age )) && [[ ${BYPASS_MIN_RELEASE_AGE:-} != 1 ]]; then + continue + fi + + if [[ -z $best_version ]] || (( $(vercmp "$version" "$best_version") > 0 )); then + best_version=$version + best_tag=$tag + best_published_at=$published_at + fi +done < <(jq -r '.[] | select((.draft or .prerelease) | not) | [.tag_name // "", .published_at // ""] | @tsv' <<<"$releases") + +if (( candidates == 0 )); then + printf 'No stable releases found for %s\n' "$REPO" >&2 + exit 1 +fi + +if [[ -z $best_version ]] || (( $(vercmp "$best_version" "$current") <= 0 )); then + echo '{}' + exit 0 +fi + +tarball=$(mktemp) +trap 'rm -f "$tarball"' EXIT +curl -fsSL -o "$tarball" "https://github.com/$REPO/archive/refs/tags/$best_tag.tar.gz" + +expected_root="flea-$best_version" +served_roots=$(tar -tzf "$tarball" | cut -d/ -f1 | sort -u) +if [[ $served_roots != "$expected_root" ]]; then + printf 'Release %s contains root %s, expected %s\n' "$best_tag" "$served_roots" "$expected_root" >&2 + exit 1 +fi + +archive_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archive.rs") +archiveops_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archiveops.rs") +run_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/run.rs") +archivereq_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archivereq.rs") +archivework_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/archivework.rs") +mediaprobe_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/mediaprobe.rs") +metareq_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/metareq.rs") +sharelink_qml=$(tar -xOzf "$tarball" "$expected_root/ui/ShareLink.qml") +copyfile_rs=$(tar -xOzf "$tarball" "$expected_root/src/backend/copyfile.rs") + +if ! grep -Fq 'a.push("--".to_string());' <<<"$archive_rs" || + ! grep -Fq 'let input = std::fs::canonicalize(input)' <<<"$archiveops_rs" || + ! grep -Fq 'if op != "compress" && op != "extract"' <<<"$run_rs$archivereq_rs" || + ! grep -Fq 'the sandbox is unavailable: bwrap or prlimit is not on PATH' <<<"$archivework_rs" || + ! grep -Fq 'if !sandbox::available()' <<<"$mediaprobe_rs" || + ! grep -Fq 'if !sandbox::available()' <<<"$metareq_rs" || + ! grep -Fq 'copyToClipboard.command = ["wl-copy", url]' <<<"$sharelink_qml" || + ! grep -Fq '.custom_flags(O_NOFOLLOW)' <<<"$copyfile_rs"; then + printf 'Release %s does not contain every required upstream security fix\n' "$best_tag" >&2 + exit 1 +fi + +jq -n \ + --arg pkgver "$best_version" \ + --arg published_at "$best_published_at" \ + --arg source "$(sha256sum "$tarball" | cut -d' ' -f1)" \ + --arg patch_2bd7cc2 "$PATCH_2BD7CC2" \ + --arg patch_27d19ca "$PATCH_27D19CA" \ + --arg patch_23290ce "$PATCH_23290CE" \ + --arg patch_b4b7ee4 "$PATCH_B4B7EE4" \ + '{pkgver: $pkgver, published_at: $published_at, sha256sums: {any: [$source, $patch_2bd7cc2, $patch_27d19ca, $patch_23290ce, $patch_b4b7ee4]}}' diff --git a/pkgbuilds/flea/PKGBUILD b/pkgbuilds/flea/PKGBUILD new file mode 100644 index 0000000..63c900f --- /dev/null +++ b/pkgbuilds/flea/PKGBUILD @@ -0,0 +1,132 @@ +# Maintainer: GM + +pkgname=flea +pkgver=0.1.2 +pkgrel=1 +pkgdesc='Fast, keyboard-first file manager for Omarchy' +arch=('x86_64') +url='https://github.com/thisisgm/flea' +license=('MIT') + +depends=( + 'bubblewrap' + 'gcc-libs' + 'glib2' + 'glibc' + 'hicolor-icon-theme' + 'omarchy' + 'quickshell' + 'qt6-multimedia' + 'qt6-webengine' + 'shared-mime-info' + 'util-linux' + 'xdg-utils' +) +makedepends=('cargo') +optdepends=( + '7zip: 7z archive support' + 'dropbox-cli: Dropbox browsing and share links' + 'ffmpeg: media metadata previews' + 'imagemagick: image conversion' + 'libarchive: archive listing and extraction' + 'tailscale: Taildrop sharing' + 'wl-clipboard: copy Dropbox share links to the clipboard' +) +conflicts=('flea-git') +options=('!debug') + +_security_patches=( + '2bd7cc207c110ae3b3ea61a4d4e42336f1815111' + '27d19ca4a38ef4a0a920fb78441ce662a0101327' + '23290ce1917b0d7f0392dd09d04bf2a7504ec93a' + 'b4b7ee47244b52457eec2874e21a8656d8ace647' +) +source=( + "$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/v$pkgver.tar.gz" + "security-2bd7cc2.patch::$url/commit/${_security_patches[0]}.patch" + "security-27d19ca.patch::$url/commit/${_security_patches[1]}.patch" + "security-23290ce.patch::$url/commit/${_security_patches[2]}.patch" + "security-b4b7ee4.patch::$url/commit/${_security_patches[3]}.patch" +) +sha256sums=( + '3788a735be2cb6eef0c1770833c82bc6591e20b7681fe02172aaea21c2a2d8fb' + 'c610a9f44294b67341940203943c9c567003b65cc436d6013cd36754379183d8' + 'e457ef17e26f70057b1184eeb938fccc5906f48a7c77f1df573d55d13a845425' + 'f8381456a3b5f39d341cc6610bf27beb8354892b17a1c8c6d7882db4e40824c3' + '46bdbe43f135a052c893b1987f8b0928736445616a84b0c65d2181ee74b21b25' +) + +prepare() { + cd "$pkgname-$pkgver" + + security_patch_present() { + case "$1" in + 2bd7cc207c110ae3b3ea61a4d4e42336f1815111) + grep -Fq 'a.push("--".to_string());' src/backend/archive.rs && + grep -Fq 'let input = std::fs::canonicalize(input)' src/backend/archiveops.rs && + grep -Fq 'if op != "compress" && op != "extract"' \ + src/backend/run.rs src/backend/archivereq.rs + ;; + 27d19ca4a38ef4a0a920fb78441ce662a0101327) + grep -Fq 'the sandbox is unavailable: bwrap or prlimit is not on PATH' src/backend/archivework.rs && + grep -Fq 'if !sandbox::available()' src/backend/mediaprobe.rs && + grep -Fq 'if !sandbox::available()' src/backend/metareq.rs + ;; + 23290ce1917b0d7f0392dd09d04bf2a7504ec93a) + grep -Fq 'copyToClipboard.command = ["wl-copy", url]' ui/ShareLink.qml + ;; + b4b7ee47244b52457eec2874e21a8656d8ace647) + grep -Fq '.custom_flags(O_NOFOLLOW)' src/backend/copyfile.rs + ;; + *) + return 1 + ;; + esac + } + + local commit patch_file + for commit in "${_security_patches[@]}"; do + patch_file="$srcdir/security-${commit:0:7}.patch" + + if patch --batch --forward --dry-run -Np1 -i "$patch_file" >/dev/null 2>&1; then + patch --batch --forward -Np1 -i "$patch_file" + elif security_patch_present "$commit"; then + printf 'Security patch %s is already present upstream\n' "$commit" + else + printf 'Security patch %s no longer applies and is not present upstream\n' "$commit" >&2 + return 1 + fi + done +} + +build() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + cargo build --frozen --release +} + +check() { + cd "$pkgname-$pkgver" + + export CARGO_TARGET_DIR=target + cargo test --frozen --release + ./tests/js.sh + ./tests/keymap-gen.sh +} + +package() { + cd "$pkgname-$pkgver" + + install -Dm755 target/release/flea "$pkgdir/usr/bin/flea" + install -Dm644 packaging/com.thisisgm.flea.desktop \ + "$pkgdir/usr/share/applications/com.thisisgm.flea.desktop" + install -Dm644 packaging/com.thisisgm.flea.svg \ + "$pkgdir/usr/share/icons/hicolor/scalable/apps/com.thisisgm.flea.svg" + install -Dm644 LICENSE "$pkgdir/usr/share/licenses/$pkgname/LICENSE" + + install -Dm644 ui/qmldir ui/*.qml -t "$pkgdir/usr/share/flea/ui" + install -Dm644 ui/js/*.js -t "$pkgdir/usr/share/flea/ui/js" + ln -s /usr/share/omarchy/shell/Commons "$pkgdir/usr/share/flea/ui/Commons" + ln -s /usr/share/omarchy/shell/Ui "$pkgdir/usr/share/flea/ui/Ui" +}