Last active
January 17, 2026 03:39
-
-
Save cvn/f24a0f228a9e42de955aaa867d40437d to your computer and use it in GitHub Desktop.
Scripts to add arbitrary file types to QLStephenSwift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Add or remove file types from QLStephenSwift and rebuild | |
| # Usage: sh qlss-add-filetype.sh <file_path> [file_path...] | |
| # 2026-01-16 v1 - Prompted by Chad von Nau | |
| # ============================================================================ | |
| # Setup | |
| # ============================================================================ | |
| # 1. Install Xcode and sign in with your Apple account | |
| # 2. Clone QLStephenSwift repository | |
| # 3. Make sure the project can build. Try qlss-fix-build.sh | |
| # 4. Update the variables below, or set them as environment variables | |
| # 5. Run this script on a file you want to preview | |
| # 6. Re-activate the extension (first time only): | |
| # https://github.com/MyCometG3/QLStephenSwift#activation-required-for-both-methods | |
| # Repository directory - Set here or via environment variable | |
| # Example: REPO_DIR=/path/to/repo sh qlss-add-filetype.sh ... | |
| REPO_DIR="${REPO_DIR:-$HOME/Repositories/QLStephenSwift}" | |
| # The app will be rebuilt and saved here: | |
| INSTALL_PATH="${INSTALL_PATH:-/Applications/QLStephenSwift.app}" | |
| # ============================================================================ | |
| # Get the file types | |
| # ============================================================================ | |
| # Exit on error | |
| set -e | |
| if [ $# -eq 0 ]; then | |
| echo "Usage: sh $0 <file_path> [file_path...]" | |
| echo "Example: sh $0 /path/to/file.yml" | |
| exit 0 | |
| fi | |
| # Track actions for verification | |
| ANY_ERROR=0 | |
| # Collect unique UTIs from all files (newline-delimited) | |
| UNIQUE_TYPES="" | |
| for FILE_PATH in "$@"; do | |
| # Check if file exists | |
| if [ ! -f "$FILE_PATH" ]; then | |
| echo "Error: File not found $FILE_PATH" | |
| ANY_ERROR=1 | |
| continue | |
| fi | |
| # Get the UTI type | |
| TYPE=$(mdls -name kMDItemContentType "$FILE_PATH" | sed -n 's/^kMDItemContentType = "\(.*\)"$/\1/p') | |
| if [ -z "$TYPE" ]; then | |
| echo "Error: Could not determine file type $FILE_PATH" | |
| ANY_ERROR=1 | |
| continue | |
| fi | |
| if ! printf '%s\n' "$UNIQUE_TYPES" | grep -qx "$TYPE"; then | |
| if [ -z "$UNIQUE_TYPES" ]; then | |
| UNIQUE_TYPES="$TYPE" | |
| else | |
| UNIQUE_TYPES="${UNIQUE_TYPES} | |
| $TYPE" | |
| fi | |
| fi | |
| done | |
| if [ $ANY_ERROR -eq 1 ]; then | |
| exit 0 | |
| fi | |
| if [ -z "$UNIQUE_TYPES" ]; then | |
| exit 0 | |
| fi | |
| # ============================================================================ | |
| # Modify the Info.plist | |
| # ============================================================================ | |
| join_types() { | |
| printf '%s\n' "$1" | awk 'NF{if(!first){printf "%s",$0; first=1} else {printf ", %s",$0}}' | |
| } | |
| INFO_PLIST="$REPO_DIR/QLStephenSwift/QLStephenSwiftPreview/Info.plist" | |
| if [ ! -f "$INFO_PLIST" ]; then | |
| echo "Error: Info.plist not found" | |
| exit 0 | |
| fi | |
| # Read existing types once | |
| EXISTING_TYPES=$(plutil -p "$INFO_PLIST" | grep -A 10 "QLSupportedContentTypes" | grep -o '"[^"]*"' | tr -d '"') | |
| # Decide action: remove all if all types already present, else add only missing | |
| ACTION="add" | |
| ALL_PRESENT=1 | |
| while IFS= read -r TYPE; do | |
| [ -z "$TYPE" ] && continue | |
| if ! printf '%s\n' "$EXISTING_TYPES" | grep -qx "$TYPE"; then | |
| ALL_PRESENT=0 | |
| break | |
| fi | |
| done <<EOF | |
| $UNIQUE_TYPES | |
| EOF | |
| if [ $ALL_PRESENT -eq 1 ]; then | |
| ACTION="remove" | |
| printf '%s' "Removing $(join_types "$UNIQUE_TYPES")... " | |
| while IFS= read -r TYPE; do | |
| [ -z "$TYPE" ] && continue | |
| ESCAPED_TYPE=$(printf '%s\n' "$TYPE" | sed 's/\./\\./g') | |
| sed -i '' "/<string>$ESCAPED_TYPE<\/string>/d" "$INFO_PLIST" | |
| done <<EOF | |
| $UNIQUE_TYPES | |
| EOF | |
| else | |
| printf '%s' "Adding $(join_types "$UNIQUE_TYPES")... " | |
| ADD_TYPES="" | |
| while IFS= read -r TYPE; do | |
| [ -z "$TYPE" ] && continue | |
| if printf '%s\n' "$EXISTING_TYPES" | grep -qx "$TYPE"; then | |
| # Skip already-present types when adding | |
| continue | |
| fi | |
| if [ -z "$ADD_TYPES" ]; then | |
| ADD_TYPES="$TYPE" | |
| else | |
| ADD_TYPES="${ADD_TYPES} | |
| $TYPE" | |
| fi | |
| done <<EOF | |
| $UNIQUE_TYPES | |
| EOF | |
| while IFS= read -r TYPE; do | |
| [ -z "$TYPE" ] && continue | |
| ARRAY_LENGTH=$(plutil -p "$INFO_PLIST" | grep -A 10 "QLSupportedContentTypes" | grep -E "^\s+[0-9]+\s+=>" | wc -l | tr -d ' ') | |
| plutil -insert NSExtension.NSExtensionAttributes.QLSupportedContentTypes.$ARRAY_LENGTH -string "$TYPE" "$INFO_PLIST" | |
| done <<EOF | |
| $ADD_TYPES | |
| EOF | |
| fi | |
| # ============================================================================ | |
| # Build the app | |
| # ============================================================================ | |
| cd "$REPO_DIR" | |
| # Pin destination to avoid xcodebuild "multiple matching destinations" warnings | |
| BUILD_ARCH="$(uname -m)" | |
| BUILD_DESTINATION="platform=macOS,arch=$BUILD_ARCH" | |
| # Debug builds allow the app to be rebuilt without having to re-register the extension | |
| BUILD_CONFIGURATION="Debug" | |
| # Build with minimal console output | |
| xcodebuild -project QLStephenSwift/QLStephenSwift.xcodeproj \ | |
| -scheme QLStephenSwift \ | |
| -configuration "$BUILD_CONFIGURATION" \ | |
| -destination "$BUILD_DESTINATION" \ | |
| -quiet \ | |
| clean build | |
| # Find the built app - try to find the most recent DerivedData directory | |
| BUILT_APP=$(find ~/Library/Developer/Xcode/DerivedData -name "QLStephenSwift.app" -type d -path "*/Build/Products/$BUILD_CONFIGURATION/*" -newer "$INFO_PLIST" 2>/dev/null | head -1) | |
| # If not found with -newer, try without | |
| if [ -z "$BUILT_APP" ]; then | |
| BUILT_APP=$(find ~/Library/Developer/Xcode/DerivedData -name "QLStephenSwift.app" -type d -path "*/Build/Products/$BUILD_CONFIGURATION/*" | head -1) | |
| fi | |
| if [ -z "$BUILT_APP" ] || [ ! -d "$BUILT_APP" ]; then | |
| echo "Error: Could not find built app" | |
| exit 0 | |
| fi | |
| # Verify the built app has necessary files | |
| if [ ! -f "$BUILT_APP/Contents/Info.plist" ] || [ ! -d "$BUILT_APP/Contents/MacOS" ]; then | |
| echo "Error: Built app appears incomplete" | |
| exit 0 | |
| fi | |
| # Copy to Applications using ditto (more reliable for app bundles) | |
| rm -rf "$INSTALL_PATH" | |
| ditto "$BUILT_APP" "$INSTALL_PATH" | |
| # Verify the types were added/removed from the Info.plist in the copied app | |
| APP_INFO_PLIST="$INSTALL_PATH/Contents/PlugIns/QLStephenSwiftPreview.appex/Contents/Info.plist" | |
| if [ -f "$APP_INFO_PLIST" ]; then | |
| APP_TYPES=$(plutil -p "$APP_INFO_PLIST" | grep -A 10 "QLSupportedContentTypes" | grep -o '"[^"]*"' | tr -d '"') | |
| while IFS= read -r TYPE; do | |
| [ -z "$TYPE" ] && continue | |
| if printf '%s\n' "$APP_TYPES" | grep -qx "$TYPE"; then | |
| if [ "$ACTION" = "remove" ]; then | |
| echo "Error: Type still present after removal $TYPE" | |
| ANY_ERROR=1 | |
| fi | |
| else | |
| if [ "$ACTION" = "add" ]; then | |
| echo "Error: Type not found after addition $TYPE" | |
| ANY_ERROR=1 | |
| fi | |
| fi | |
| done <<EOF | |
| $UNIQUE_TYPES | |
| EOF | |
| fi | |
| # Success message with checkmark | |
| if [ $ANY_ERROR -eq 0 ]; then | |
| echo "✓ OK" | |
| fi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Set IDs in QLStephenSwift so local signing works, then test the build | |
| # Usage: sh qlss-fix-build.sh /path/to/repo | |
| # 2026-01-16 v1 - Prompted by Chad von Nau | |
| # ============================================================================ | |
| # Setup | |
| # ============================================================================ | |
| # Repository directory - first argument | |
| REPO_DIR="$1" | |
| # Development Team ID - set here or leave empty to auto-detect | |
| DEVELOPMENT_TEAM="${DEVELOPMENT_TEAM:-}" | |
| # ============================================================================ | |
| # Main | |
| # ============================================================================ | |
| set -e | |
| if [ -z "$REPO_DIR" ]; then | |
| echo "Usage: $0 /path/to/repo" | |
| exit 0 | |
| fi | |
| # Try to detect Development Team ID from Xcode preferences | |
| detect_development_team() { | |
| team_id=$(defaults read "$HOME/Library/Preferences/com.apple.dt.Xcode.plist" IDEProvisioningTeamByIdentifier 2>/dev/null | \ | |
| grep -A 3 "teamID" | \ | |
| grep "teamID" | \ | |
| head -1 | \ | |
| sed -n 's/.*teamID = \([A-Z0-9]*\);/\1/p') | |
| if [ -n "$team_id" ] && [ ${#team_id} -eq 10 ]; then | |
| echo "$team_id" | |
| else | |
| return 1 | |
| fi | |
| } | |
| if [ -z "$DEVELOPMENT_TEAM" ]; then | |
| if ! DEVELOPMENT_TEAM=$(detect_development_team); then | |
| echo "Error: Could not detect Development Team ID. Open Xcode and sign in, or set DEVELOPMENT_TEAM variable." | |
| exit 0 | |
| fi | |
| fi | |
| # Validate team ID (typical 10-char Apple team ID) | |
| if ! printf '%s' "$DEVELOPMENT_TEAM" | grep -Eq '^[A-Z0-9]{10}$'; then | |
| echo "Error: DEVELOPMENT_TEAM must be 10 characters (A-Z, 0-9)" | |
| exit 0 | |
| fi | |
| # Replace author IDs with user's team ID | |
| if grep -Eq "com\.mycometg3(\.[A-Z0-9]{10})?\.qlstephenswift" "$REPO_DIR/QLStephenSwift/QLStephenSwift.xcodeproj/project.pbxproj" 2>/dev/null && \ | |
| ! grep -Fq "com.mycometg3.${DEVELOPMENT_TEAM}.qlstephenswift" "$REPO_DIR/QLStephenSwift/QLStephenSwift.xcodeproj/project.pbxproj" 2>/dev/null | |
| then | |
| find "$REPO_DIR/QLStephenSwift" -type f \( -name "*.entitlements" -o -name "AppConstants.swift" -o -name "project.pbxproj" \) \ | |
| -exec sed -i '' \ | |
| -E "s/com\.mycometg3(\.[A-Z0-9]{10})?\.qlstephenswift/com.mycometg3.$DEVELOPMENT_TEAM.qlstephenswift/g" \ | |
| {} + | |
| echo "Updated IDs to com.mycometg3.$DEVELOPMENT_TEAM.qlstephenswift" | |
| fi | |
| # Persist DEVELOPMENT_TEAM in the project so other scripts can build | |
| PROJECT_PBXPROJ="$REPO_DIR/QLStephenSwift/QLStephenSwift.xcodeproj/project.pbxproj" | |
| if grep -q "DEVELOPMENT_TEAM" "$PROJECT_PBXPROJ" 2>/dev/null; then | |
| sed -i '' -E "s/DEVELOPMENT_TEAM = [^;]+;/DEVELOPMENT_TEAM = $DEVELOPMENT_TEAM;/g" "$PROJECT_PBXPROJ" | |
| echo "Updated DEVELOPMENT_TEAM to $DEVELOPMENT_TEAM" | |
| else | |
| echo "Warning: DEVELOPMENT_TEAM not found in project.pbxproj; not updated." | |
| fi | |
| cd "$REPO_DIR" | |
| # Pin destination to avoid xcodebuild "multiple matching destinations" warnings | |
| BUILD_ARCH="$(uname -m)" | |
| BUILD_DESTINATION="platform=macOS,arch=$BUILD_ARCH" | |
| printf '%s' "Testing build... " | |
| # Build (same base flags as ql-add-filetype.sh, plus signing setup) | |
| xcodebuild -project QLStephenSwift/QLStephenSwift.xcodeproj \ | |
| -scheme QLStephenSwift \ | |
| -configuration Debug \ | |
| -destination "$BUILD_DESTINATION" \ | |
| -quiet \ | |
| CODE_SIGN_STYLE=Automatic \ | |
| -allowProvisioningUpdates \ | |
| clean build | |
| echo "✓ OK" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment