#!/bin/zsh function at-repos-env() { host=syu.is did=did:plc:6qyecktefllvenje24fcxnie repos=( "https://github.com/did-method-plc/did-method-plc" "https://github.com/bluesky-social/indigo" "https://github.com/bluesky-social/atproto" "https://github.com/bluesky-social/social-app" "https://github.com/bluesky-social/feed-generator" "https://github.com/bluesky-social/ozone" "https://github.com/bluesky-social/jetstream" ) services=( "bsky" "plc" "pds" "jetstream" "bgs" "ozone" "social-app" ) d=${0:a:h} dh=${0:a:h:h} name=${host%%.*} domain=${host##*.} dport=5000 } # Arrays for patch management typeset -a FAILED_PATCHES # Patch file lists typeset -a PATCH_FILES_CURL PATCH_FILES_CURL=( "4367-atproto-services-bsky-api.diff:https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/bsky/api.js:services/bsky/api.js" "4367-atproto-services-pds-index.diff:https://raw.githubusercontent.com/bluesky-social/atproto/refs/heads/main/services/pds/index.js:services/pds/index.js" ) typeset -a PATCH_FILES PATCH_FILES=( "170-pds-oauth-same-site-fix.patch" "8980-social-app-disable-proxy.diff" "disable-statsig-sdk.diff" "140-social-app-yarn-network-timeout.patch" "130-atproto-ozone-enable-daemon-v2.patch" ) function at-repos-clone() { if [ ! -d $d/repos ];then mkdir -p $d/repos fi cd $d/repos for ((i=1; i<=${#repos}; i++)); do repo=${repos[$i]} echo $repo if [ ! -d $d/repos/${repo##*/} ];then git clone $repo fi done if [ ! -f $d/repos/feed-generator/Dockerfile ] && [ -f $d/docker/feed/Dockerfile ];then cp -rf $d/docker/feed/Dockerfile $d/repos/feed-generator/ fi } function at-repos-pull() { cd $d/repos for ((i=1; i<=${#repos}; i++)); do repo=${repos[$i]} echo $repo if [ -d $d/repos/${repo##*/} ];then cd $d/repos/${repo##*/} git stash if ! git pull;then rm -rf $d/repos/${repo##*/} at-repos-clone fi fi rv=$(echo "$repos_v" | jq -r ".[\"${repo##*/}\"]") if [ "$rv" != "null" ];then cd $d/repos/${repo##*/} git reset --hard $rv cd .. fi done cd $d } function at-repos-social-app-avatar-write() { did_admin=did:plc:6qyecktefllvenje24fcxnie dt=$d/repos/social-app/src cd $dt grep -R syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/syu.is/${host}/g" grep -R web.syu.is .|cut -d : -f 1|sort -u|xargs sed -i "s/web.syu.is/web.${host}/g" f=$dt/lib/constants.ts sed -i "s#export const BSKY_SERVICE = 'https://bsky.social'#export const BSKY_SERVICE = 'https://${host}'#g" $f sed -i "s#export const BSKY_SERVICE_DID = 'did:web:bsky.social'#export const BSKY_SERVICE_DID = 'did:web:${host}'#g" $f sed -i "s#export const PUBLIC_BSKY_SERVICE = 'https://public.api.bsky.app'#export const PUBLIC_BSKY_SERVICE = 'https://bsky.${host}'#g" $f sed -i "s#export const PUBLIC_APPVIEW = 'https://api.bsky.app'#export const PUBLIC_APPVIEW = 'https://bsky.${host}'#g" $f sed -i "s#export const PUBLIC_APPVIEW_DID = 'did:web:api.bsky.app'#export const PUBLIC_APPVIEW_DID = 'did:web:bsky.${host}'#g" $f f=$dt/view/icons/Logotype.tsx o=$d/icons/Logotype.tsx cp -rf $o $f f=$dt/view/com/util/UserAvatar.tsx curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/view/com/util/UserAvatar.tsx -o $f sed -i "s#/img/avatar/plain/#https://cdn.web.syu.is/img/avatar/plain/#g" $f sed -i "s#/img/avatar_thumbnail/plain/#https://bsky.${host}/img/avatar/plain/#g" $f sed -i "s#source={{uri: avatar}}#source={{ uri: hackModifyThumbnailPath(avatar, 1 > 0), }}#g" $f curl -sL https://raw.githubusercontent.com/bluesky-social/social-app/refs/heads/main/src/lib/strings/url-helpers.ts -o $dt/lib/strings/url-helpers.ts sed -i "s#https://go.web.syu.is/redirect?u=\${encodeURIComponent(url)}#\${url}#g" $dt/lib/strings/url-helpers.ts grep -R $did_admin .|cut -d : -f 1|sort -u|xargs sed -i "s/${did_admin}/${did}/g" } # Common patch function with status detection function apply-patch() { local patch_name=$1 local target_dir=$2 local patch_file=$3 echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "📝 Patch: ${patch_name}" echo " Target: ${target_dir}" echo " File: ${patch_file}" pushd ${target_dir} > /dev/null # Check if patch is already applied (reverse dry-run succeeds) if patch --dry-run -p1 -R < ${patch_file} > /dev/null 2>&1; then echo "✅ Already applied - skipping" popd > /dev/null echo "" return 0 fi # Check if patch can be applied (forward dry-run succeeds) if patch --dry-run -p1 < ${patch_file} > /dev/null 2>&1; then echo "🔧 Applying patch..." if patch -p1 < ${patch_file}; then echo "✅ Applied successfully" popd > /dev/null echo "" return 0 else echo "❌ Failed to apply" FAILED_PATCHES+=("${patch_name} (${patch_file})") popd > /dev/null echo "" return 1 fi else echo "⚠️ Cannot apply - file may have been modified" echo " Please check manually" FAILED_PATCHES+=("${patch_name} (${patch_file}) - file modified") popd > /dev/null echo "" return 1 fi } # Function to display failed patches summary function show-failed-patches() { if [ ${#FAILED_PATCHES[@]} -eq 0 ]; then echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "✅ All patches applied successfully!" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" return 0 fi echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "⚠️ FAILED PATCHES SUMMARY" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" echo "The following patches could not be applied:" echo "" for failed_patch in "${FAILED_PATCHES[@]}"; do echo " ❌ ${failed_patch}" done echo "" echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" echo "" } # Helper function for applying patches function patch-apply() { local name=$1 local target=$2 local patch_file=$3 apply-patch "${name}" "$d/repos/${target}" "$d/patching/${patch_file}" } # Helper function for patches with curl download function patch-apply-with-curl() { local name=$1 local target=$2 local patch_file=$3 local download_url=$4 local download_target=$5 curl -sL "${download_url}" -o "$d/repos/${target}/${download_target}" apply-patch "${name}" "$d/repos/${target}" "$d/patching/${patch_file}" } # Auto-apply patches from list function at-repos-patch-apply-all() { # Apply curl patches for patch_info in "${PATCH_FILES_CURL[@]}"; do local filename="${patch_info%%:*}" local rest="${patch_info#*:}" local download_url="${rest%%:*}" local download_target="${rest#*:}" local title="${filename%.*}" local repo="" # Determine repo from filename if [[ $filename == *"atproto"* ]]; then repo="atproto" elif [[ $filename == *"pds"* ]]; then repo="atproto" fi patch-apply-with-curl "$title" "$repo" "$filename" "$download_url" "$download_target" done # Apply regular patches for filename in "${PATCH_FILES[@]}"; do local title="${filename%.*}" local repo="" # Determine repo from filename if [[ $filename == *"social-app"* || $filename == *"statsig"* ]]; then repo="social-app" elif [[ $filename == *"atproto"* ]]; then repo="atproto" elif [[ $filename == *"pds"* ]]; then repo="atproto" fi patch-apply "$title" "$repo" "$filename" done } function at-repos-ozone-patch() { cd $d/repos d_=$d/repos/ozone rm -rf ${d_} git clone https://github.com/bluesky-social/ozone apply-patch "Ozone enable daemon" "${d_}" "$d/patching/122-ozone-enable-daemon.diff" if [ -f "$d/patching/150-ozone-plc-fix.patch" ]; then apply-patch "Ozone plc fix" "${d_}" "$d/patching/150-ozone-plc-fix.patch" fi if [ -f "$d/patching/160-ozone-oauth-redirect-fix.patch" ]; then apply-patch "Ozone oauth redirect fix" "${d_}" "$d/patching/160-ozone-oauth-redirect-fix.patch" fi # Apply constants fix and do additional sed replacements pushd ${d_} > /dev/null if [ -f "$d/patching/121-ozone-constants-fix.patch" ]; then patch -p1 < "$d/patching/121-ozone-constants-fix.patch" 2>/dev/null || true fi # Replace process.env with env() sed -i 's/process\.env\.\(NEXT_PUBLIC_[A-Z_]*\)/env('\''\1'\'')/g' lib/constants.ts 2>/dev/null || true sed -i 's/process\.env\.NODE_ENV/env('\''NODE_ENV'\'')/g' lib/constants.ts 2>/dev/null || true # Add missing SOCIAL_APP_DOMAIN constant after SOCIAL_APP_URL sed -i '/^export const SOCIAL_APP_URL =/,/^$/{ /^$/a\ export const SOCIAL_APP_DOMAIN =\ env('\''NEXT_PUBLIC_SOCIAL_APP_DOMAIN'\'') || '\''bsky.app'\''\ }' lib/constants.ts 2>/dev/null || true # Fix multiline process.env patterns sed -i '/^export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/,/^ : 7$/ { s/^export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/export const NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = env('\''NEXT_PUBLIC_NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS'\'')/ /^ \.NEXT_PUBLIC_NEW_ACCOUNT_MARKER_THRESHOLD_IN_DAYS$/d }' lib/constants.ts 2>/dev/null || true sed -i '/^export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/,/^ : 30$/ { s/^export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = process\.env$/export const YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS = env('\''NEXT_PUBLIC_YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS'\'')/ /^ \.NEXT_PUBLIC_YOUNG_ACCOUNT_MARKER_THRESHOLD_IN_DAYS$/d }' lib/constants.ts 2>/dev/null || true sed -i '/^export const HIGH_PROFILE_FOLLOWER_THRESHOLD = process\.env$/,/^ : Infinity$/ { s/^export const HIGH_PROFILE_FOLLOWER_THRESHOLD = process\.env$/export const HIGH_PROFILE_FOLLOWER_THRESHOLD = env('\''NEXT_PUBLIC_HIGH_PROFILE_FOLLOWER_THRESHOLD'\'')/ /^ \.NEXT_PUBLIC_HIGH_PROFILE_FOLLOWER_THRESHOLD$/d }' lib/constants.ts 2>/dev/null || true # Fix parseInt() to handle undefined by adding || '' sed -i "s/parseInt(env('\([^']*\)'))/parseInt(env('\1') || '0')/g" lib/constants.ts 2>/dev/null || true popd > /dev/null } function at-repos-build-docker-atproto() { cd $d docker image prune -a if [ -z "$1" ];then for ((i=1; i<=${#services}; i++)); do service=${services[$i]} docker compose build --no-cache $service done else docker compose build --no-cache $1 fi } function at-repos-push-reset() { if [ -n "$(docker ps -q -f name=registry)" ]; then echo "Registry is already running." docker restart registry docker stop registry docker rm registry docker volume rm registry-data 2>/dev/null || true fi docker run -d -p ${dport}:${dport} --name registry \ --restart=always \ -v registry-data:/var/lib/registry \ registry:2 } function at-repos-push-docker() { if [ -z "$1" ];then for ((i=1; i<=${#services}; i++)); do service=${services[$i]} docker tag at-${service}:latest localhost:${dport}/${service}:latest docker push localhost:${dport}/${service}:latest if [ "$service" == "ozone" ]];then docker tag at-${service}-web:latest localhost:${dport}/${service}-web:latest docker push localhost:${dport}/${service}-web:latest fi done else docker tag at-${1}:latest localhost:${dport}/${1}:latest docker push localhost:${dport}/${1}:latest fi } function at-repos-pull-docker() { cd $d docker image prune -a docker compose up -d --pull always } at-repos-env case "$1" in pull) at-repos-clone at-repos-pull exit ;; patch) at-repos-social-app-avatar-write at-repos-patch-apply-all at-repos-ozone-patch show-failed-patches exit ;; build) at-repos-build-docker-atproto $2 exit ;; push) at-repos-push-docker $2 exit ;; reset) at-repos-push-reset exit ;; down) cd $d;docker compose down exit ;; esac case "`cat /etc/hostname`" in at) at-repos-pull-docker exit ;; *) at-repos-clone at-repos-pull at-repos-social-app-avatar-write at-repos-patch-apply-all at-repos-ozone-patch show-failed-patches at-repos-build-docker-atproto at-repos-push-docker cd $d; docker compose down ;; esac