Skip to content

feat(delight): add Yoink Receipt, weekly digest, and site change monitor #24

feat(delight): add Yoink Receipt, weekly digest, and site change monitor

feat(delight): add Yoink Receipt, weekly digest, and site change monitor #24

Workflow file for this run

name: Build Yoinkit
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
workflow_dispatch:
permissions:
contents: write
jobs:
build-macos:
runs-on: macos-latest
strategy:
matrix:
target: [aarch64-apple-darwin, x86_64-apple-darwin]
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- name: Install frontend dependencies
run: pnpm install
- name: Build wget from source (static)
run: |
# Install build dependencies
brew install autoconf automake pkg-config
# Download wget source
curl -L -o wget-1.24.5.tar.gz https://ftp.gnu.org/gnu/wget/wget-1.24.5.tar.gz
tar xzf wget-1.24.5.tar.gz
cd wget-1.24.5
# Build with static linking - bundle OpenSSL and other deps
# Use Apple's SecureTransport instead of OpenSSL to avoid dynamic deps
./configure \
--with-ssl=openssl \
--with-included-regex \
--without-libidn \
--without-libuuid \
--disable-nls \
--disable-pcre \
--disable-pcre2 \
OPENSSL_CFLAGS="$(pkg-config --cflags openssl 2>/dev/null || echo '-I/opt/homebrew/opt/openssl@3/include')" \
OPENSSL_LIBS="$(pkg-config --libs --static openssl 2>/dev/null || echo '-L/opt/homebrew/opt/openssl@3/lib -lssl -lcrypto')" \
LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib" \
CFLAGS="-I/opt/homebrew/opt/openssl@3/include"
make -j$(sysctl -n hw.ncpu)
# Copy the built binary
mkdir -p ../apps/desktop/src-tauri/bin
cp src/wget ../apps/desktop/src-tauri/bin/wget
chmod +x ../apps/desktop/src-tauri/bin/wget
cd ..
# Recursively bundle ALL dylib dependencies (including transitive)
WGET_BIN=apps/desktop/src-tauri/bin/wget
LIBS_DIR=apps/desktop/src-tauri/bin/libs
mkdir -p "$LIBS_DIR"
# Function to get non-system dylib deps of a binary
get_deps() {
otool -L "$1" | awk '{print $1}' | tail -n +2 | grep -v '/usr/lib\|/System\|@executable_path\|@rpath\|@loader_path'
}
# Recursively collect all dylibs needed
QUEUE=$(get_deps "$WGET_BIN")
PROCESSED=""
while [ -n "$QUEUE" ]; do
NEXT_QUEUE=""
for lib in $QUEUE; do
LIBNAME=$(basename "$lib")
# Skip if already processed
echo "$PROCESSED" | grep -q "$LIBNAME" && continue
PROCESSED="$PROCESSED $LIBNAME"
if [ -f "$lib" ]; then
echo "Bundling: $lib -> $LIBS_DIR/$LIBNAME"
cp "$lib" "$LIBS_DIR/$LIBNAME"
chmod 644 "$LIBS_DIR/$LIBNAME"
# Get this lib's own deps and add to queue
SUB_DEPS=$(get_deps "$lib")
for sub in $SUB_DEPS; do
SUB_NAME=$(basename "$sub")
echo "$PROCESSED" | grep -q "$SUB_NAME" || NEXT_QUEUE="$NEXT_QUEUE $sub"
done
else
echo "WARNING: dependency not found: $lib"
fi
done
QUEUE="$NEXT_QUEUE"
done
# Rewrite all paths in the wget binary
for lib in $(get_deps "$WGET_BIN"); do
LIBNAME=$(basename "$lib")
install_name_tool -change "$lib" "@executable_path/libs/$LIBNAME" "$WGET_BIN"
done
# Rewrite all paths in every bundled dylib
for bundled_lib in "$LIBS_DIR"/*.dylib; do
# Set its own id
install_name_tool -id "@executable_path/libs/$(basename "$bundled_lib")" "$bundled_lib"
# Rewrite references to other bundled libs
for dep in $(otool -L "$bundled_lib" | awk '{print $1}' | tail -n +2 | grep -v '/usr/lib\|/System\|@executable_path\|@rpath\|@loader_path'); do
DEP_NAME=$(basename "$dep")
if [ -f "$LIBS_DIR/$DEP_NAME" ]; then
install_name_tool -change "$dep" "@executable_path/libs/$DEP_NAME" "$bundled_lib"
fi
done
done
# Ad-hoc codesign everything
for f in "$LIBS_DIR"/*.dylib; do
codesign --force --sign - "$f"
done
codesign --force --sign - "$WGET_BIN"
# Strip quarantine
xattr -cr apps/desktop/src-tauri/bin/
# Verify — no non-system, non-@executable_path deps should remain
echo "=== Verifying wget dependencies ==="
otool -L "$WGET_BIN"
echo "=== Verifying bundled libs ==="
for f in "$LIBS_DIR"/*.dylib; do
echo "--- $(basename "$f") ---"
otool -L "$f"
done
echo "=== Checking for any remaining Homebrew paths ==="
REMAINING=$(otool -L "$WGET_BIN" "$LIBS_DIR"/*.dylib 2>/dev/null | grep '/opt/homebrew' || true)
if [ -n "$REMAINING" ]; then
echo "ERROR: Homebrew paths still found!"
echo "$REMAINING"
exit 1
else
echo "All clear — no Homebrew paths remain."
fi
- name: Bundle yt-dlp and ffmpeg
run: |
BIN_DIR=apps/desktop/src-tauri/bin
# Download yt-dlp (standalone binary)
echo "=== Downloading yt-dlp ==="
if [[ "${{ matrix.target }}" == "aarch64-apple-darwin" ]]; then
curl -L -o "$BIN_DIR/yt-dlp" "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos"
else
curl -L -o "$BIN_DIR/yt-dlp" "https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_macos"
fi
chmod +x "$BIN_DIR/yt-dlp"
# Download ffmpeg (static build)
echo "=== Downloading ffmpeg ==="
if [[ "${{ matrix.target }}" == "aarch64-apple-darwin" ]]; then
curl -L -o ffmpeg.zip "https://evermeet.cx/ffmpeg/getrelease/zip"
else
curl -L -o ffmpeg.zip "https://evermeet.cx/ffmpeg/getrelease/zip"
fi
unzip -o ffmpeg.zip -d "$BIN_DIR/"
chmod +x "$BIN_DIR/ffmpeg"
rm ffmpeg.zip
# Codesign both binaries
codesign --force --sign - "$BIN_DIR/yt-dlp"
codesign --force --sign - "$BIN_DIR/ffmpeg"
# Strip quarantine attributes
xattr -cr "$BIN_DIR/yt-dlp"
xattr -cr "$BIN_DIR/ffmpeg"
echo "=== Verifying binaries ==="
file "$BIN_DIR/yt-dlp"
file "$BIN_DIR/ffmpeg"
ls -la "$BIN_DIR/"
- name: Build Tauri app
env:
TAURI_SIGNING_PRIVATE_KEY: ""
TARGET: ${{ matrix.target }}
run: |
cd apps/desktop
pnpm exec tauri build --target "$TARGET"
- name: Fix code signing on bundled binaries
run: |
# Find the built .app bundle
APP_PATH=$(find apps/desktop/src-tauri/target -name "Yoinkit.app" -type d | head -1)
if [ -n "$APP_PATH" ]; then
echo "Found app at: $APP_PATH"
# Strip quarantine from everything
xattr -cr "$APP_PATH"
# Re-sign bundled libs first, then wget, then the whole app
LIBS_IN_APP="$APP_PATH/Contents/Resources/bin/libs"
if [ -d "$LIBS_IN_APP" ]; then
find "$LIBS_IN_APP" -name "*.dylib" -exec codesign --force --sign - {} \;
fi
WGET_IN_APP="$APP_PATH/Contents/Resources/bin/wget"
if [ -f "$WGET_IN_APP" ]; then
codesign --force --sign - "$WGET_IN_APP"
fi
YTDLP_IN_APP="$APP_PATH/Contents/Resources/bin/yt-dlp"
if [ -f "$YTDLP_IN_APP" ]; then
codesign --force --sign - "$YTDLP_IN_APP"
fi
FFMPEG_IN_APP="$APP_PATH/Contents/Resources/bin/ffmpeg"
if [ -f "$FFMPEG_IN_APP" ]; then
codesign --force --sign - "$FFMPEG_IN_APP"
fi
# Re-sign the entire app bundle
codesign --force --deep --sign - "$APP_PATH"
fi
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: yoinkit-${{ matrix.target }}
path: |
apps/desktop/src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg
apps/desktop/src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app
create-release:
needs: build-macos
runs-on: macos-latest
if: startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/checkout@v4
- name: Download arm64 artifact
uses: actions/download-artifact@v4
with:
name: yoinkit-aarch64-apple-darwin
path: artifacts/arm64
- name: Download x86_64 artifact
uses: actions/download-artifact@v4
with:
name: yoinkit-x86_64-apple-darwin
path: artifacts/x86_64
- name: List artifacts
run: find artifacts -type f | head -20
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
files: |
artifacts/arm64/**/*.dmg
artifacts/x86_64/**/*.dmg
generate_release_notes: true
draft: false