Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,79 @@ jobs:
fi
xcodebuild -version

- name: Fetch the engine bundle from the private engine repo
# Makes a nightly behave like an official release: installer.sh finds the
# engine inside Resources and sets it up offline instead of building or
# downloading wine itself. Installing = replacing deps/wine-unified
# wholesale, which is what installer.sh already does (rm -rf then extract).
#
# WINE_ENGINE_TOKEN is a fine-grained PAT with Contents: read-only on the
# engine repo -- it is private, so the workflow's own GITHUB_TOKEN cannot
# see it. Absent secret = launcher-only build rather than a hard failure,
# so a fork without the secret still produces a usable nightly.
id: payload
env:
GH_TOKEN: ${{ secrets.WINE_ENGINE_TOKEN }}
ENGINE_REPO: mont127/MacNdCheese-WineEngine-PRIVATE
run: |
set -euo pipefail
if [ -z "${GH_TOKEN:-}" ]; then
echo "::warning::WINE_ENGINE_TOKEN not set - building launcher-only (no bundled engine)"
echo "bundled=false" >> "$GITHUB_OUTPUT"
exit 0
fi
mkdir -p payload

# Newest release carrying an engine asset, chosen by ASSET NAME rather than
# `--latest`: engine builds are often prereleases and the repo also publishes
# d3d-pack-only releases, either of which would make --latest pick the wrong one.
TAG="$(gh release list --repo "$ENGINE_REPO" --limit 100 --json tagName \
--jq '.[].tagName' | while read -r t; do
if gh api "repos/$ENGINE_REPO/releases/tags/$t" \
--jq '.assets[].name' 2>/dev/null | grep -q 'unified-osx64\.tar\.xz'; then
echo "$t"; break
fi
done)"
[ -n "$TAG" ] || { echo "::error::no unified engine asset found in $ENGINE_REPO"; exit 1; }
echo "engine release: $TAG"

gh release download "$TAG" --repo "$ENGINE_REPO" \
--pattern '*unified-osx64.tar.xz' --dir payload
mv payload/*unified-osx64.tar.xz payload/wine-unified-bundle.tar.xz

# The engine bundle carries mnc-d3d/ itself, so nothing else to fetch. Verify
# it is complete: a pack missing either half of the D3DMetal runtime loads
# fine and then dies on "Failed to dlopen D3DMetal" with no hint a file is
# missing, so fail here rather than shipping it.
for req in mnc-d3d/d3d11_dxmt.dll mnc-d3d/dxgi_dxmt.dll \
mnc-d3d/libd3dshared.dylib mnc-d3d/D3DMetal.framework \
loader/wine server/wineserver; do
tar -tJf payload/wine-unified-bundle.tar.xz "wine-unified/$req" >/dev/null 2>&1 \
|| { echo "::error::engine bundle incomplete: $req missing"; exit 1; }
done

echo "engine_tag=$TAG" >> "$GITHUB_OUTPUT"
echo "bundled=true" >> "$GITHUB_OUTPUT"
ls -lh payload/

- name: Build universal DMG
env:
MNC_PAYLOAD_DIR: payload
run: |
chmod +x buildapp.sh
bash buildapp.sh universal
ls -la *.dmg

- name: Fail if the DMG exceeds GitHub's 2 GB asset limit
# A bundled nightly is ~1.9 GB; uploads over 2 GB are rejected outright and
# the failure surfaces late and confusingly, so check it up front.
run: |
SZ=$(stat -f%z "MacNdCheese Launcher.dmg" 2>/dev/null || echo 0)
echo "DMG size: $((SZ/1048576)) MB"
if [ "$SZ" -gt 2147483648 ]; then
echo "::error::DMG is $((SZ/1048576)) MB, over GitHub's 2048 MB asset limit"
exit 1
fi

- name: Compute tag and release notes
id: tag
Expand Down
23 changes: 23 additions & 0 deletions buildapp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,29 @@ for img in Steam.png Wine.png Setting.png Add.png icon.png; do
fi
done

# Engine payloads (wine-unified-bundle.zip, mnc-d3d/, redist/, ...). An official
# release ships these inside Resources -- that is what makes installer.sh able to
# set the engine up offline instead of building or downloading wine itself. They
# are far too large for git, so they are supplied out of band: point
# MNC_PAYLOAD_DIR at a directory containing them (the nightly workflow fills it
# from the engine repo's releases).
#
# Whatever is in that directory is copied verbatim, so a payload set can grow
# without touching this script. Absent = a launcher-only build, which is the
# pre-existing behaviour and still perfectly usable -- installer.sh just falls
# back to its other sources.
PAYLOAD_DIR="${MNC_PAYLOAD_DIR:-payload}"
if [ -d "$PAYLOAD_DIR" ] && [ -n "$(ls -A "$PAYLOAD_DIR" 2>/dev/null)" ]; then
echo "Bundling engine payloads from $PAYLOAD_DIR:"
for item in "$PAYLOAD_DIR"/*; do
[ -e "$item" ] || continue
cp -R "$item" "$RESOURCES/"
echo " + $(basename "$item") ($(du -sh "$item" 2>/dev/null | cut -f1))"
done
else
echo "No engine payloads ($PAYLOAD_DIR absent/empty) — building launcher-only."
fi

# Extract App Intents metadata so Siri/Apple Intelligence can discover shortcuts.
# App Intents definitions don't vary by CPU arch, so for a universal build one
# representative triple (arm64) is enough — this only affects Siri phrase
Expand Down
42 changes: 33 additions & 9 deletions installer.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2233,13 +2233,21 @@ locate_wine_unified_bundle() {
script_path="$0"
case "$script_path" in /*) ;; *) script_path="$PWD/$script_path" ;; esac
script_dir="$(cd "$(dirname "$script_path")" 2>/dev/null && pwd)" || script_dir=""
# .tar.xz is what the engine repo publishes (and what the nightly bundles);
# .zip is the historical hand-made bundle. Both are accepted.
candidates="
${WINE_UNIFIED_BUNDLE_PATH:-}
${RESOURCES_DIR:-}/wine-unified-bundle.tar.xz
${RESOURCES_DIR:-}/wine-unified-bundle.zip
${script_dir}/wine-unified-bundle.tar.xz
${script_dir}/wine-unified-bundle.zip
${script_dir}/../Resources/wine-unified-bundle.tar.xz
${script_dir}/../Resources/wine-unified-bundle.zip
${script_dir}/../../Resources/wine-unified-bundle.tar.xz
${script_dir}/../../Resources/wine-unified-bundle.zip
$HOME/macndcheese/wine-unified-bundle.tar.xz
$HOME/macndcheese/wine-unified-bundle.zip
$HOME/Library/Application Support/MacNCheese/wine-unified-bundle.tar.xz
$HOME/Library/Application Support/MacNCheese/wine-unified-bundle.zip
"
while IFS= read -r c; do
Expand All @@ -2250,7 +2258,7 @@ $candidates
EOF
for root in /Applications "$HOME/Applications" "$HOME/Downloads"; do
[ -d "$root" ] || continue
found="$(find "$root" -maxdepth 5 -name 'wine-unified-bundle.zip' -type f 2>/dev/null | head -n1)"
found="$(find "$root" -maxdepth 5 \( -name 'wine-unified-bundle.tar.xz' -o -name 'wine-unified-bundle.zip' \) -type f 2>/dev/null | head -n1)"
[ -n "$found" ] && [ -f "$found" ] && { printf '%s' "$found"; return 0; }
done
return 1
Expand Down Expand Up @@ -2309,16 +2317,32 @@ install_wine_unified() {
echo "Using unified wine bundle: $bundle"
rm -rf "$dst"
mkdir -p "$dst"
if command -v unzip >/dev/null 2>&1; then
unzip -q "$bundle" -d "$dst" || { echo "Failed to unzip unified wine bundle"; exit 1; }
elif [ -x "$SEVENZ_BIN" ]; then
"$SEVENZ_BIN" x -y -o"$dst" "$bundle" >/dev/null || { echo "Failed to extract unified wine bundle"; exit 1; }
else
echo "Neither unzip nor 7z available to extract the bundle"; exit 1
fi
case "$bundle" in
*.tar.xz|*.txz)
# The tarball wraps everything in one top-level dir (wine-unified/), but
# $dst IS that dir -- strip a level or it ends up nested twice.
tar -xJf "$bundle" -C "$dst" --strip-components=1 \
|| { echo "Failed to untar unified wine bundle"; exit 1; }
;;
*)
if command -v unzip >/dev/null 2>&1; then
unzip -q "$bundle" -d "$dst" || { echo "Failed to unzip unified wine bundle"; exit 1; }
elif [ -x "$SEVENZ_BIN" ]; then
"$SEVENZ_BIN" x -y -o"$dst" "$bundle" >/dev/null || { echo "Failed to extract unified wine bundle"; exit 1; }
else
echo "Neither unzip nor 7z available to extract the bundle"; exit 1
fi
;;
esac
find "$dst" -name 'wine' -type f -exec chmod +x {} \; 2>/dev/null || true
xattr -dr com.apple.quarantine "$dst" 2>/dev/null || true
stage_unified_d3d_pack "$dst"
# The engine bundle now ships mnc-d3d/ itself; only stage from Resources when
# it didn't (older hand-made .zip bundles).
if [ -f "$dst/mnc-d3d/d3d11.dll" ]; then
echo "install_wine_unified: d3d pack came with the bundle ($(ls "$dst/mnc-d3d" 2>/dev/null | wc -l | tr -d ' ') entries)"
else
stage_unified_d3d_pack "$dst"
fi
stage_redist_pack
sign_unified_wine "$dst"
echo "install_wine_unified: done ($(du -sh "$dst" 2>/dev/null | cut -f1))"
Expand Down
Loading