This commit is contained in:
Ryan Hughes committed 2026-05-08 01:04:51 -04:00
1 parent 6df117702c
commit 1da6706ed8
12 files changed
+6

No files matched your search

-25
View File
@@ -1,25 +0,0 @@
pkgbase = lmstudio-bin
pkgdesc = LM Studio - A desktop app for exploring and running large language models locally
pkgver = 0.4.12
pkgrel = 1
url = https://lmstudio.ai
install = lmstudio-bin.install
arch = x86_64
license = EULA
depends = fuse2
depends = zlib
depends = hicolor-icon-theme
depends = gtk3
depends = nss
depends = libxcrypt-compat
provides = lmstudio
conflicts = lmstudio
options = !strip
source = https://installers.lmstudio.ai/linux/x64/0.4.12-1/LM-Studio-0.4.12-1-x64.AppImage
source = lmstudio.png
source = lmstudio.desktop
sha256sums = 53b4c990c52a98be1693bef3708376ff8205cfb6abb6f560d2c691123a06cbc2
sha256sums = 9f791789c959a11316328692807737a5f1bc1c170ae99ec04c56bfd8ee8263e5
sha256sums = d5ac11aae025cd1cc0f9aa0713ee427dc6f46e4d1adac2a5948f93d8bbca705f
pkgname = lmstudio-bin
-9
View File
@@ -1,9 +0,0 @@
pkg/
src/
*.AppImage
*.tar.gz
*.tar.xz
*.log
squashfs-root/
__pycache__/
*.pkg.tar.zst
@@ -1,102 +0,0 @@
PROJECT BLUEPRINT: lmstudio-bin Maintenance Framework
1. Executive Summary
Project Name: lmstudio-bin
Target Platform: Arch Linux (AUR)
Upstream Software: LM Studio (Proprietary AppImage)
This project provides an Arch User Repository (AUR) package for LM Studio. It employs a "Maintenance Bridge" architecture to convert dynamic upstream releases into deterministic, security-verified Arch Linux packages while adhering strictly to Arch Linux packaging standards for binary software.
2. Core Objectives
Correct Naming Convention: Utilize the -bin suffix as mandated by AUR guidelines for software distributed as pre-compiled binaries.
Replacement of Legacy Packages: Explicitly provide and conflict with the abandoned lmstudio package to ensure a clean migration path for the community.
Security & Determinism: Implement strict SHA256 checksum verification for every release, preventing "moving target" build failures.
Full Desktop Integration: Ensure high-resolution icon support, XDG-compliant desktop entries, and taskbar grouping (StartupWMClass).
3. Technical Architecture
A. The Maintenance Engine (update_package.py)
The heart of the project is a Python-based automation script that manages the lifecycle of a release.
URL Resolution: Follows the latest redirect to identify the specific build version.
Verification: Downloads the AppImage and calculates the SHA256 hash.
Template Rendering: Injects metadata into PKGBUILD.template to produce a valid PKGBUILD.
Metadata Generation: Automatically updates .SRCINFO for AUR indexing.
B. Packaging Logic (PKGBUILD)
The build process follows a specific lifecycle to ensure stability:
Extraction Stage: Uses --appimage-extract within a temporary sandbox to access internal assets.
Icon Strategy (Static Vendoring):
* Static Icon: The high-resolution (512x512) icon is vendored as `lmstudio.png` in the base repository.
* Optimization: Icons must be optimized to stay under the 500KB AUR repository blob limit.
* Rationale: Static vendoring prevents build-time extraction failures and ensures consistent visual presentation across versions without relying on internal AppImage layout stability.
Binary Placement: Installs the AppImage to /opt/lm-studio/ and provides a symbolic link at /usr/bin/lm-studio.
C. Lifecycle Management (.install)
The .install script ensures the host system recognizes changes immediately upon installation, upgrade, or removal by triggering:
update-desktop-database (Applications menu)
gtk-update-icon-cache (Visual assets)
4. Automation Workflow (For Orchestrator Reference)
When updating to a new version, the orchestrator should follow this sequence:
Sync: Pull latest changes from the AUR remote.
Execute: Run ./update_package.py to fetch upstream changes and regenerate files.
Validate: * Verify PKGBUILD version matches upstream.
Run makepkg -f to test the build locally.
Inspect pkg/ directory to ensure icon assets are present at /usr/share/icons/hicolor/512x512/apps/lmstudio-bin.png.
Publish: * Refresh .SRCINFO (makepkg --printsrcinfo > .SRCINFO).
Commit PKGBUILD, .SRCINFO, lmstudio.desktop, lmstudio.png, and lmstudio-bin.install.
Push to origin master.
5. Git Hygiene & AUR Constraints
* Blob Limit: The AUR imposes a strict 512KB limit on individual file blobs. All vendored assets (like `lmstudio.png`) must be optimized to comply.
* Tracking Restrictions: Only source-of-truth files and metadata are permitted in the repository. Tracking of `src/` or `pkg/` directories is strictly prohibited.
* Verification: Use `verify_git_hygiene.sh` to audit the repository state before pushing to the AUR remote.
6. Directory Structure
.
├── PKGBUILD # Generated Arch Linux build script
├── PKGBUILD.template # Blueprint for the maintenance engine
├── .SRCINFO # AUR metadata index (auto-generated)
├── lmstudio.desktop # Desktop integration file
├── lmstudio.png # Static vendored icon (512x512)
├── lmstudio-bin.install # Post-install hooks for cache clearing
├── update_package.py # The Maintenance Engine script
├── verify_git_hygiene.sh # Hygiene enforcement script
└── LMStudio-bin Blueprint.md # This reference document
7. Maintenance Contacts & Authority
Maintainer: madgoat (AUR)
Package URL: https://aur.archlinux.org/packages/lmstudio-bin
-43
View File
@@ -1,43 +0,0 @@
# Maintainer: noureddinex
pkgname=lmstudio-bin
pkgver=0.4.12
pkgrel=1
_pkgver=${pkgver}-${pkgrel}
pkgdesc="LM Studio - A desktop app for exploring and running large language models locally"
arch=('x86_64')
url="https://lmstudio.ai"
license=('EULA')
depends=('fuse2' 'zlib' 'hicolor-icon-theme' 'gtk3' 'nss' 'libxcrypt-compat')
options=('!strip')
install=lmstudio-bin.install
provides=(lmstudio)
conflicts=(lmstudio)
source=("https://installers.lmstudio.ai/linux/x64/${_pkgver}/LM-Studio-${_pkgver}-x64.AppImage"
"lmstudio.png"
"lmstudio.desktop")
sha256sums=('53b4c990c52a98be1693bef3708376ff8205cfb6abb6f560d2c691123a06cbc2'
'9f791789c959a11316328692807737a5f1bc1c170ae99ec04c56bfd8ee8263e5'
'd5ac11aae025cd1cc0f9aa0713ee427dc6f46e4d1adac2a5948f93d8bbca705f')
prepare() {
chmod +x "${srcdir}/${source[0]##*/}"
}
package() {
# Create installation directory
install -dm755 "$pkgdir/opt/lm-studio"
# Install AppImage
install -Dm755 "${srcdir}/${source[0]##*/}" "$pkgdir/opt/lm-studio/lm-studio.AppImage"
# Install static icon
install -Dm644 "${srcdir}/lmstudio.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/lmstudio-bin.png"
install -Dm644 "${srcdir}/lmstudio.png" "${pkgdir}/usr/share/pixmaps/lmstudio-bin.png"
# Desktop entry
install -Dm644 "$srcdir/lmstudio.desktop" "$pkgdir/usr/share/applications/lmstudio.desktop"
# Symlink to binary
install -dm755 "$pkgdir/usr/bin"
ln -s /opt/lm-studio/lm-studio.AppImage "$pkgdir/usr/bin/lm-studio"
}
@@ -1,39 +0,0 @@
pkgname=lmstudio-bin
pkgver={{version}}
pkgrel=1
pkgdesc="LM Studio - A desktop app for exploring and running large language models locally"
arch=('x86_64')
url="https://lmstudio.ai"
license=('EULA')
depends=('fuse2' 'zlib' 'hicolor-icon-theme' 'gtk3' 'nss' 'libxcrypt-compat')
options=('!strip')
install=lmstudio-bin.install
source=("{{url}}"
"lmstudio.png"
"lmstudio.desktop")
sha256sums=('{{sha256}}'
'{{icon_sha256}}'
'SKIP')
prepare() {
chmod +x "${srcdir}/${source[0]##*/}"
}
package() {
# Create installation directory
install -dm755 "$pkgdir/opt/lm-studio"
# Install AppImage
install -Dm755 "${srcdir}/${source[0]##*/}" "$pkgdir/opt/lm-studio/lm-studio.AppImage"
# Install static icon
install -Dm644 "${srcdir}/lmstudio.png" "${pkgdir}/usr/share/icons/hicolor/512x512/apps/lmstudio-bin.png"
install -Dm644 "${srcdir}/lmstudio.png" "${pkgdir}/usr/share/pixmaps/lmstudio-bin.png"
# Desktop entry
install -Dm644 "$srcdir/lmstudio.desktop" "$pkgdir/usr/share/applications/lmstudio.desktop"
# Symlink to binary
install -dm755 "$pkgdir/usr/bin"
ln -s /opt/lm-studio/lm-studio.AppImage "$pkgdir/usr/bin/lm-studio"
}
@@ -1,14 +0,0 @@
post_install() {
update-desktop-database -q
gtk-update-icon-cache -ftq /usr/share/icons/hicolor
}
post_upgrade() {
update-desktop-database -q
gtk-update-icon-cache -ftq /usr/share/icons/hicolor
}
post_remove() {
update-desktop-database -q
gtk-update-icon-cache -ftq /usr/share/icons/hicolor
}
@@ -1,12 +0,0 @@
[Desktop Entry]
Name=LM Studio
GenericName=Large Language Model Studio
Comment=A desktop app for exploring and running large language models locally
Exec=lm-studio %U
Icon=lmstudio-bin
Type=Application
Categories=Development;ArtificialIntelligence;
Terminal=false
StartupNotify=true
StartupWMClass=LM Studio
MimeType=text/plain;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

@@ -1,204 +0,0 @@
#!/usr/bin/env python3
"""
LM Studio PKGBUILD Maintenance Engine
Resolves dynamic upstream link, extracts version, calculates SHA256,
and generates PKGBUILD from template.
Updated to handle:
- AppImage file with calculated sha256
- .desktop file with 'SKIP' (locally managed)
- Static icon (lmstudio.png) with calculated sha256
- .install file staging and template rendering
"""
import re
import hashlib
import requests
import subprocess
import tarfile
from pathlib import Path
from typing import Tuple, List
def resolve_download_url(redirect_url: str = "https://lmstudio.ai/download/latest/linux/x64") -> str:
"""
Follow redirects to resolve the final download URL.
Uses HEAD request to avoid downloading the full file.
"""
print(f"Resolving download URL: {redirect_url}")
session = requests.Session()
response = session.head(redirect_url, allow_redirects=True)
response.raise_for_status()
final_url = response.url
print(f"Resolved URL: {final_url}")
return final_url
def extract_version(filename: str) -> str:
"""
Extract semantic version from filename using regex.
Expected format: LM-Studio-X.Y.Z-x64.AppImage or LM-Studio-X.Y.Z-R-x64.AppImage
Extracts only X.Y.Z portion (pkgrel handled separately in PKGBUILD).
"""
match = re.search(r'LM-Studio-([\d.]+)-(?:\d+-)?x64\.AppImage', filename)
if match:
return match.group(1)
raise ValueError(f"Could not extract version from filename: {filename}")
def calculate_sha256(file_path: str) -> str:
"""
Calculate SHA256 checksum of a file.
Handles both AppImage and icon files with identical hashing logic.
"""
print(f"Calculating SHA256 for: {file_path}")
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
checksum = sha256_hash.hexdigest()
print(f"SHA256: {checksum}")
return checksum
def download_file(url: str, file_path: str) -> None:
"""
Download file from URL if it doesn't already exist.
"""
path = Path(file_path)
if path.exists():
print(f"File already exists: {file_path}")
return
print(f"Downloading: {url}")
response = requests.get(url, stream=True)
response.raise_for_status()
with open(file_path, "wb") as f:
for chunk in response.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
print(f"Downloaded: {file_path}")
def stage_install_file() -> None:
"""
Ensure lmstudio-bin.install file exists and is staged.
"""
install_path = Path("/home/madgoat/Documents/LMStudio-bin Aur/lmstudio-bin.install")
if install_path.exists():
print(f"✓ Install file present: {install_path}")
else:
print(f"Warning: Install file not found at {install_path}")
def render_template(template_path: str, output_path: str, version: str, url: str, sha256: str, icon_sha256: str) -> None:
"""
Render PKGBUILD from template by replacing placeholders.
Placeholders:
- {{version}}: Semantic version extracted from filename
- {{url}}: Resolved download URL
- {{sha256}}: SHA256 checksum of the AppImage
- {{icon_sha256}}: SHA256 checksum of the static icon (lmstudio.png)
The template includes sha256sums array with appimage hash, icon hash, and SKIP for .desktop file.
"""
print(f"Rendering template: {template_path} -> {output_path}")
with open(template_path, "r") as f:
content = f.read()
content = content.replace("{{version}}", version)
content = content.replace("{{url}}", url)
content = content.replace("{{sha256}}", sha256)
content = content.replace("{{icon_sha256}}", icon_sha256)
with open(output_path, "w") as f:
f.write(content)
print(f"Generated: {output_path}")
def generate_srcinfo() -> None:
"""
Generate .SRCINFO using makepkg --printsrcinfo.
"""
print("Generating .SRCINFO...")
result = subprocess.run(
["makepkg", "--printsrcinfo"],
capture_output=True,
text=True,
cwd="/home/madgoat/Documents/LMStudio-bin Aur"
)
if result.returncode != 0:
raise RuntimeError(f"makepkg failed: {result.stderr}")
with open("/home/madgoat/Documents/LMStudio-bin Aur/.SRCINFO", "w") as f:
f.write(result.stdout)
print("Generated: .SRCINFO")
def main():
"""
Main maintenance engine workflow.
"""
print("=== LM Studio PKGBUILD Maintenance Engine ===\n")
# Change to workspace directory
import os
os.chdir("/home/madgoat/Documents/LMStudio-bin Aur")
# Step 1: Resolve download URL
final_url = resolve_download_url()
# Step 2: Extract filename and version
filename = final_url.split("/")[-1]
version = extract_version(filename)
print(f"Detected version: {version}\n")
# Step 3: Download file if missing
download_file(final_url, filename)
# Step 4: Calculate SHA256 for AppImage
sha256 = calculate_sha256(filename)
print()
# Step 5: Calculate SHA256 for static icon (lmstudio.png)
# Uses identical hashing logic as AppImage
icon_hash = calculate_sha256("lmstudio.png")
print()
# Step 6: Stage install file
stage_install_file()
print()
# Step 7: Render PKGBUILD from template
# The template uses {{sha256}} for the AppImage hash and {{icon_sha256}} for icon hash
# and the sha256sums array in the template includes both hashes plus 'SKIP' for .desktop file
render_template("PKGBUILD.template", "PKGBUILD", version, final_url, sha256, icon_hash)
print()
# Step 8: Generate .SRCINFO
generate_srcinfo()
print("\n=== Maintenance Engine Complete ===")
print(f"Version: {version}")
print(f"URL: {final_url}")
print(f"AppImage SHA256: {sha256}")
print(f"Icon SHA256: {icon_hash}")
print(f"SHA256sums: ('{sha256}' '{icon_hash}' 'SKIP')")
if __name__ == "__main__":
main()
@@ -1,100 +0,0 @@
#!/bin/bash
# Git Hygiene Verification Guard
# Updated: Explicitly allows lmstudio.png, verifies exclusion of src/ and pkg/ from history and staged index
echo "=== Git Hygiene Verification Guard ==="
echo ""
# Color codes
RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
NC="\033[0m"
FAIL=0
# Check 1: Verify lmstudio.png is NOT ignored (explicitly allowed)
echo "Check 1: Verifying lmstudio.png is tracked/allowed..."
if git ls-files lmstudio.png 2>/dev/null | grep -q "lmstudio.png"; then
echo -e "${GREEN}✓ PASSED: lmstudio.png is tracked${NC}"
elif ! git check-ignore lmstudio.png 2>/dev/null; then
echo -e "${GREEN}✓ PASSED: lmstudio.png is not ignored${NC}"
else
echo -e "${YELLOW}⚠ WARNING: lmstudio.png appears to be in .gitignore${NC}"
fi
echo ""
# Check 2: Verify src/ folder is NOT in git history
echo "Check 2: Verifying src/ folder is NOT in git history..."
SRC_IN_HISTORY=$(git log --all --full-history --name-only --format="" -- 'src/' 2>/dev/null | wc -l)
if [ "$SRC_IN_HISTORY" -gt 0 ]; then
echo -e "${RED}✗ FAILED: src/ folder found in git history${NC}"
FAIL=1
else
echo -e "${GREEN}✓ PASSED: src/ folder is clean from history${NC}"
fi
echo ""
# Check 3: Verify pkg/ folder is NOT in git history
echo "Check 3: Verifying pkg/ folder is NOT in git history..."
PKG_IN_HISTORY=$(git log --all --full-history --name-only --format="" -- 'pkg/' 2>/dev/null | wc -l)
if [ "$PKG_IN_HISTORY" -gt 0 ]; then
echo -e "${RED}✗ FAILED: pkg/ folder found in git history${NC}"
FAIL=1
else
echo -e "${GREEN}✓ PASSED: pkg/ folder is clean from history${NC}"
fi
echo ""
# Check 4: Verify src/ folder is NOT in staged index
echo "Check 4: Verifying src/ folder is NOT in staged index..."
SRC_STAGED=$(git diff --cached --name-only -- 'src/' 2>/dev/null | wc -l)
if [ "$SRC_STAGED" -gt 0 ]; then
echo -e "${RED}✗ FAILED: src/ folder has staged files${NC}"
git diff --cached --name-only -- 'src/'
FAIL=1
else
echo -e "${GREEN}✓ PASSED: src/ folder is clean from staged index${NC}"
fi
echo ""
# Check 5: Verify pkg/ folder is NOT in staged index
echo "Check 5: Verifying pkg/ folder is NOT in staged index..."
PKG_STAGED=$(git diff --cached --name-only -- 'pkg/' 2>/dev/null | wc -l)
if [ "$PKG_STAGED" -gt 0 ]; then
echo -e "${RED}✗ FAILED: pkg/ folder has staged files${NC}"
git diff --cached --name-only -- 'pkg/'
FAIL=1
else
echo -e "${GREEN}✓ PASSED: pkg/ folder is clean from staged index${NC}"
fi
echo ""
# Check 6: Verify no AppImages or packages are staged
echo "Check 6: Verifying NO binary extensions are staged..."
BINARY_STAGED=$(git diff --cached --name-only | grep -E "\.(AppImage|zst|tar\.gz)$" | wc -l)
if [ "$BINARY_STAGED" -gt 0 ]; then
echo -e "${RED}✗ FAILED: Binary files are staged!${NC}"
git diff --cached --name-only | grep -E "\.(AppImage|zst|tar\.gz)$"
FAIL=1
else
echo -e "${GREEN}✓ PASSED: No binary extensions staged${NC}"
fi
echo ""
# Check 7: Review staged changes
echo "Check 7: Reviewing staged changes summary..."
git diff --cached --stat
echo ""
# Final result
echo "=== Verification Summary ==="
if [ "$FAIL" -eq 0 ]; then
echo -e "${GREEN}✓ All hygiene checks PASSED${NC}"
echo "Repository is ready for commit."
exit 0
else
echo -e "${RED}✗ Some hygiene checks FAILED${NC}"
echo "Please fix the issues before committing."
exit 1
fi
@@ -1,49 +0,0 @@
#!/bin/bash
# LM Studio AUR Package Verification Script
# Updated: Checks for vendored lmstudio.png in root, makes /usr/share checks informative in dev environments
echo "=== LM Studio AUR Verification ==="
echo ""
# 1. Vendored Icon Existence Check (Primary)
echo "[1/4] Checking Vendored Icon..."
if [ -f "./lmstudio.png" ]; then
echo "✓ Found vendored lmstudio.png in root"
else
echo "✗ MISSING: vendored lmstudio.png in root"
exit 1
fi
echo ""
# 2. System Icon Installation Check (Informative)
echo "[2/4] Checking System Icon Installation..."
if [ -f "/usr/share/pixmaps/lmstudio-bin.png" ]; then
echo "✓ Found lmstudio-bin pixmap icon in /usr/share"
else
echo "ℹ INFO: lmstudio-bin pixmap not in /usr/share (expected in non-installed dev environment)"
fi
echo ""
# 3. Desktop File Verification
echo "[3/4] Verifying Desktop Entry..."
ICON_NAME=$(grep "^Icon=" lmstudio.desktop | cut -d'=' -f2)
if [ "$ICON_NAME" == "lmstudio-bin" ]; then
echo "✓ Desktop Icon name matches: $ICON_NAME"
else
echo "✗ Desktop Icon mismatch: $ICON_NAME (expected lmstudio-bin)"
exit 1
fi
echo ""
# 4. PKGBUILD Consistency
echo "[4/4] Verifying PKGBUILD Icon Mapping..."
grep -q "lmstudio.png" PKGBUILD
if [ $? -eq 0 ]; then
echo "✓ PKGBUILD contains lmstudio.png mapping"
else
echo "✗ PKGBUILD missing icon installation logic"
exit 1
fi
echo ""
echo "=== SYSTEM GREEN ==="