Files
music-assistant/music-assistant-build-files.md
2026-09-13 20:21:10 +02:00

326 lines
11 KiB
Markdown

# Build Files Template for music-assistant
Use this document as the prompt/instruction file for an agent when you create the
`music-assistant` Swift macOS app project.
The target project does not need to match the Servery path. These files should be
created in the root of the future `music-assistant` project, next to
`Package.swift`.
## Agent Request
Copy this request into the agent working inside the new project:
```text
Create the same build-command structure for this Swift macOS app.
App name:
music-assistant
Product name:
music-assistant
Bundle identifier:
com.music-assistant.app
Requirements:
1. Create `Makefile` with `build`, `dev`, and `test` commands.
2. Create `clean_rebuild.sh` for a clean release build, packaging, signing,
installing into `/Applications`, and launching the app.
3. Create `dev_rebuild.sh` for a fast incremental debug build, packaging,
installing into `/Applications`, and launching the app.
4. Assume these files live in the project root next to `Package.swift`.
5. Use the app icon at `Resources/music-assistant.icns`.
6. If the app has additional resources, copy them into
`Contents/Resources` during packaging.
7. Make both shell scripts executable.
8. Keep the implementation small and local. Do not refactor unrelated project
files.
9. After creating the files, verify that the scripts reference the correct app
name, product name, bundle identifier, icon path, and install path.
```
## Makefile
Create this file as `Makefile`:
```makefile
.PHONY: build dev test
build:
./clean_rebuild.sh
dev:
./dev_rebuild.sh
test:
swift test
```
## dev_rebuild.sh
Create this file as `dev_rebuild.sh`:
```zsh
#!/bin/zsh
# Fast development rebuild: incremental DEBUG build, repackage, relaunch.
#
# This skips `swift package clean` and release-mode optimizations, so it reuses
# the incremental build cache and finishes quickly. Use it while iterating.
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")" && pwd)"
APP_NAME="music-assistant"
PRODUCT_NAME="music-assistant"
APP_BUNDLE_NAME="${APP_NAME}.app"
BUILD_DIR="${PROJECT_ROOT}/.build"
APP_BUNDLE="${BUILD_DIR}/${APP_BUNDLE_NAME}"
INSTALL_PATH="/Applications/${APP_BUNDLE_NAME}"
CONTENTS_DIR="${APP_BUNDLE}/Contents"
MACOS_DIR="${CONTENTS_DIR}/MacOS"
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
APP_ICON="${PROJECT_ROOT}/Resources/music-assistant.icns"
fail() {
print -u2 -- "Error: $1"
exit 1
}
step() {
print -- "==> $1"
}
[[ -f "${PROJECT_ROOT}/Package.swift" ]] || fail "Package.swift was not found in ${PROJECT_ROOT}."
[[ -f "${APP_ICON}" ]] || fail "App icon was not found at ${APP_ICON}."
command -v swift >/dev/null 2>&1 || fail "Swift is required. Install Xcode or the Xcode Command Line Tools."
command -v codesign >/dev/null 2>&1 || fail "codesign is required and should be available with Xcode."
cd "${PROJECT_ROOT}"
step "Stopping any running ${APP_NAME} application"
if pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1; then
pkill -x "${PRODUCT_NAME}" || fail "Could not stop the running ${APP_NAME} application."
for _ in {1..20}; do
pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1 || break
sleep 0.1
done
pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1 && fail "${APP_NAME} is still running. Quit it and retry."
fi
step "Building ${PRODUCT_NAME} in debug mode (incremental)"
swift build || fail "Debug build failed. Review the Swift compiler diagnostics above."
BIN_PATH="$(swift build --show-bin-path 2>/dev/null | tail -n1)"
SOURCE_EXECUTABLE="${BIN_PATH}/${PRODUCT_NAME}"
[[ -x "${SOURCE_EXECUTABLE}" ]] || fail "Built executable was not found at ${SOURCE_EXECUTABLE}."
step "Packaging ${APP_BUNDLE_NAME}"
rm -rf "${APP_BUNDLE}"
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
cp "${SOURCE_EXECUTABLE}" "${MACOS_DIR}/${PRODUCT_NAME}"
cp "${APP_ICON}" "${RESOURCES_DIR}/music-assistant.icns"
cat > "${CONTENTS_DIR}/Info.plist" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>music-assistant</string>
<key>CFBundleExecutable</key>
<string>music-assistant</string>
<key>CFBundleIdentifier</key>
<string>com.music-assistant.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIconFile</key>
<string>music-assistant</string>
<key>CFBundleName</key>
<string>music-assistant</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
PLIST
plutil -lint "${CONTENTS_DIR}/Info.plist" >/dev/null || fail "Generated Info.plist is invalid."
codesign --force --sign - --timestamp=none "${APP_BUNDLE}" || fail "Ad-hoc signing of ${APP_BUNDLE_NAME} failed."
[[ -x "${MACOS_DIR}/${PRODUCT_NAME}" ]] || fail "Packaged executable is missing."
step "Installing ${APP_BUNDLE_NAME} in /Applications"
rm -rf "${INSTALL_PATH}"
ditto "${APP_BUNDLE}" "${INSTALL_PATH}" || fail "Could not install ${APP_BUNDLE_NAME} in /Applications. Check permissions and retry."
step "Launching ${APP_NAME}"
open -n "${INSTALL_PATH}" || fail "${APP_NAME} was installed but could not be opened."
print -- "==> ${APP_NAME} was rebuilt (debug), installed, and launched successfully."
```
## clean_rebuild.sh
Create this file as `clean_rebuild.sh`:
```zsh
#!/bin/zsh
# Build, package, install, and launch the music-assistant macOS application.
set -euo pipefail
PROJECT_ROOT="$(cd "$(dirname "$0")" && pwd)"
APP_NAME="music-assistant"
PRODUCT_NAME="music-assistant"
APP_BUNDLE_NAME="${APP_NAME}.app"
BUILD_DIR="${PROJECT_ROOT}/.build"
APP_BUNDLE="${BUILD_DIR}/${APP_BUNDLE_NAME}"
INSTALL_PATH="/Applications/${APP_BUNDLE_NAME}"
CONTENTS_DIR="${APP_BUNDLE}/Contents"
MACOS_DIR="${CONTENTS_DIR}/MacOS"
RESOURCES_DIR="${CONTENTS_DIR}/Resources"
APP_ICON="${PROJECT_ROOT}/Resources/music-assistant.icns"
fail() {
print -u2 -- "Error: $1"
exit 1
}
step() {
print -- "==> $1"
}
[[ -f "${PROJECT_ROOT}/Package.swift" ]] || fail "Package.swift was not found in ${PROJECT_ROOT}."
[[ -f "${APP_ICON}" ]] || fail "App icon was not found at ${APP_ICON}."
command -v swift >/dev/null 2>&1 || fail "Swift is required. Install Xcode or the Xcode Command Line Tools."
command -v codesign >/dev/null 2>&1 || fail "codesign is required and should be available with Xcode."
cd "${PROJECT_ROOT}"
step "Stopping any running ${APP_NAME} application"
if pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1; then
pkill -x "${PRODUCT_NAME}" || fail "Could not stop the running ${APP_NAME} application."
for _ in {1..20}; do
pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1 || break
sleep 0.1
done
pgrep -x "${PRODUCT_NAME}" >/dev/null 2>&1 && fail "${APP_NAME} is still running. Quit it and retry."
fi
step "Removing stale installed copy"
if [[ -e "${INSTALL_PATH}" ]]; then
rm -rf "${INSTALL_PATH}" || fail "Could not remove ${INSTALL_PATH}. Check permissions and retry."
fi
step "Cleaning local Swift build artifacts"
swift package clean || fail "Swift Package Manager could not clean the local build artifacts."
step "Building ${PRODUCT_NAME} in release mode"
BIN_PATH="$(swift build -c release --show-bin-path 2>/dev/null | tail -n1)"
SOURCE_EXECUTABLE="${BIN_PATH}/${PRODUCT_NAME}"
built=0
for attempt in 1 2 3; do
swift build -c release || fail "Release build failed. Review the Swift compiler diagnostics above."
if [[ -x "${SOURCE_EXECUTABLE}" ]]; then built=1; break; fi
print -- "==> Executable not present after build (attempt ${attempt}); retrying"
sleep 1
done
[[ "${built}" -eq 1 ]] || fail "Built executable was not found at ${SOURCE_EXECUTABLE}."
step "Packaging ${APP_BUNDLE_NAME}"
rm -rf "${APP_BUNDLE}"
mkdir -p "${MACOS_DIR}" "${RESOURCES_DIR}"
cp "${SOURCE_EXECUTABLE}" "${MACOS_DIR}/${PRODUCT_NAME}"
cp "${APP_ICON}" "${RESOURCES_DIR}/music-assistant.icns"
cat > "${CONTENTS_DIR}/Info.plist" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>music-assistant</string>
<key>CFBundleExecutable</key>
<string>music-assistant</string>
<key>CFBundleIdentifier</key>
<string>com.music-assistant.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleIconFile</key>
<string>music-assistant</string>
<key>CFBundleName</key>
<string>music-assistant</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>14.0</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>
PLIST
plutil -lint "${CONTENTS_DIR}/Info.plist" >/dev/null || fail "Generated Info.plist is invalid."
codesign --force --sign - --timestamp=none "${APP_BUNDLE}" || fail "Ad-hoc signing of ${APP_BUNDLE_NAME} failed."
codesign --verify --deep --strict "${APP_BUNDLE}" || fail "Packaged ${APP_BUNDLE_NAME} failed code-signature verification."
[[ -x "${MACOS_DIR}/${PRODUCT_NAME}" ]] || fail "Packaged executable is missing."
step "Installing ${APP_BUNDLE_NAME} in /Applications"
ditto "${APP_BUNDLE}" "${INSTALL_PATH}" || fail "Could not install ${APP_BUNDLE_NAME} in /Applications. Check permissions and retry."
INSTALLED_EXECUTABLE="${INSTALL_PATH}/Contents/MacOS/${PRODUCT_NAME}"
[[ -x "${INSTALLED_EXECUTABLE}" ]] || fail "Installed executable is missing at ${INSTALLED_EXECUTABLE}."
SOURCE_HASH="$(shasum -a 256 "${MACOS_DIR}/${PRODUCT_NAME}" | awk '{print $1}')"
INSTALLED_HASH="$(shasum -a 256 "${INSTALLED_EXECUTABLE}" | awk '{print $1}')"
[[ "${SOURCE_HASH}" == "${INSTALLED_HASH}" ]] || fail "Installed executable does not match the newly built executable."
step "Launching ${APP_NAME}"
open -n "${INSTALL_PATH}" || fail "${APP_NAME} was installed but could not be opened."
print -- "==> ${APP_NAME} was rebuilt, installed, and launched successfully."
```
## Required Terminal Step
After the files are created in the new project, run:
```bash
chmod +x clean_rebuild.sh dev_rebuild.sh
```
Then use:
```bash
make dev
make build
make test
```
## Notes
- The future project must have `Package.swift`.
- The future project must have an icon at `Resources/music-assistant.icns`.
- If the executable product name in `Package.swift` is different, change
`PRODUCT_NAME` in both scripts.
- If the app needs permissions, add the matching usage-description keys to the
generated `Info.plist`.