Skip to main content

max / audiofiles

Add build scripts for macOS, Linux, and Windows distribution build-macos.sh (sign + notarize + staple DMG), build-deb.sh, build-appimage.sh, build-windows.sh (cargo-xwin cross-compile). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-03-21 16:40 UTC
Commit: 0ee23a371797c76b780886aa6e10bb8a624e0403
Parent: a3bca54
7 files changed, +331 insertions, -0 deletions
M .gitignore +1
@@ -29,3 +29,4 @@ recursive fonts.085/
29 29 .vscode/
30 30 *.swp
31 31 *.swo
32 + dist/*.exe
@@ -0,0 +1,9 @@
1 + [Desktop Entry]
2 + Type=Application
3 + Name=AudioFiles
4 + Comment=Sample manager with content-addressed storage
5 + Exec=audiofiles-app
6 + Icon=audiofiles
7 + Categories=Audio;AudioVideo;
8 + MimeType=audio/wav;audio/x-wav;audio/flac;audio/x-flac;audio/mpeg;audio/ogg;audio/aiff;audio/x-aiff;
9 + Terminal=false
@@ -0,0 +1,60 @@
1 + #!/usr/bin/env bash
2 + set -euo pipefail
3 +
4 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5 + PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6 + DIST_DIR="$SCRIPT_DIR"
7 + TOOLS_DIR="$DIST_DIR/tools"
8 + ARCH=$(uname -m)
9 +
10 + # Read version from Cargo.toml
11 + VERSION=$(grep '^version' "$PROJECT_DIR/crates/audiofiles-app/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
12 + echo "Building AudioFiles v${VERSION} AppImage (${ARCH})"
13 +
14 + # Step 1: Build release binary
15 + echo "==> Building release binary..."
16 + cd "$PROJECT_DIR"
17 + cargo build --release -p audiofiles-app
18 +
19 + # Step 2: Create AppDir
20 + echo "==> Assembling AppDir..."
21 + APPDIR="$DIST_DIR/AppDir"
22 + rm -rf "$APPDIR"
23 + mkdir -p "$APPDIR/usr/bin"
24 +
25 + cp "$PROJECT_DIR/target/release/audiofiles-app" "$APPDIR/usr/bin/audiofiles-app"
26 + cp "$DIST_DIR/audiofiles.desktop" "$APPDIR/audiofiles.desktop"
27 + cp "$DIST_DIR/audiofiles.png" "$APPDIR/audiofiles.png"
28 +
29 + # AppRun wrapper
30 + cat > "$APPDIR/AppRun" << 'APPRUN'
31 + #!/usr/bin/env bash
32 + SELF="$(readlink -f "$0")"
33 + HERE="${SELF%/*}"
34 + exec "${HERE}/usr/bin/audiofiles-app" "$@"
35 + APPRUN
36 + chmod +x "$APPDIR/AppRun"
37 +
38 + # Step 3: Get appimagetool
39 + mkdir -p "$TOOLS_DIR"
40 + APPIMAGETOOL="$TOOLS_DIR/appimagetool-${ARCH}.AppImage"
41 + if [ ! -x "$APPIMAGETOOL" ]; then
42 + echo "==> Downloading appimagetool..."
43 + curl -fSL "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${ARCH}.AppImage" \
44 + -o "$APPIMAGETOOL"
45 + chmod +x "$APPIMAGETOOL"
46 + fi
47 +
48 + # Step 4: Build AppImage
49 + echo "==> Creating AppImage..."
50 + APPIMAGE_NAME="AudioFiles-${VERSION}-${ARCH}.AppImage"
51 + APPIMAGE_PATH="$DIST_DIR/$APPIMAGE_NAME"
52 + rm -f "$APPIMAGE_PATH"
53 +
54 + ARCH="$ARCH" "$APPIMAGETOOL" "$APPDIR" "$APPIMAGE_PATH"
55 +
56 + # Cleanup
57 + rm -rf "$APPDIR"
58 +
59 + echo ""
60 + echo "Done: $APPIMAGE_PATH"
@@ -0,0 +1,75 @@
1 + #!/usr/bin/env bash
2 + set -euo pipefail
3 +
4 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5 + PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6 + DIST_DIR="$SCRIPT_DIR"
7 + ARCH=$(dpkg --print-architecture 2>/dev/null || echo "amd64")
8 +
9 + # Read version from Cargo.toml
10 + VERSION=$(grep '^version' "$PROJECT_DIR/crates/audiofiles-app/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
11 + echo "Building AudioFiles v${VERSION} .deb (${ARCH})"
12 +
13 + # Step 1: Build release binary
14 + echo "==> Building release binary..."
15 + cd "$PROJECT_DIR"
16 + cargo build --release -p audiofiles-app
17 +
18 + # Step 2: Assemble deb tree
19 + echo "==> Assembling package..."
20 + PKG_DIR="$DIST_DIR/.deb-staging/audiofiles_${VERSION}_${ARCH}"
21 + rm -rf "$DIST_DIR/.deb-staging"
22 + mkdir -p "$PKG_DIR/DEBIAN"
23 + mkdir -p "$PKG_DIR/usr/bin"
24 + mkdir -p "$PKG_DIR/usr/share/applications"
25 + mkdir -p "$PKG_DIR/usr/share/icons/hicolor/256x256/apps"
26 +
27 + cp "$PROJECT_DIR/target/release/audiofiles-app" "$PKG_DIR/usr/bin/audiofiles-app"
28 + cp "$DIST_DIR/audiofiles.desktop" "$PKG_DIR/usr/share/applications/audiofiles.desktop"
29 + cp "$DIST_DIR/audiofiles.png" "$PKG_DIR/usr/share/icons/hicolor/256x256/apps/audiofiles.png"
30 +
31 + # Compute installed size in KB
32 + INSTALLED_SIZE=$(du -sk "$PKG_DIR" | cut -f1)
33 +
34 + # Runtime deps:
35 + # libasound2 - ALSA (cpal audio playback)
36 + # libgl1 - OpenGL (eframe glow backend)
37 + # libxcb1 - X11 connection (egui/winit)
38 + # libxkbcommon0 - Keyboard input
39 + # libssl3 - TLS (reqwest native-tls) -- libssl3 for Ubuntu 22.04+
40 + # libgtk-3-0 - File dialogs (rfd/nfd)
41 + cat > "$PKG_DIR/DEBIAN/control" << CONTROL
42 + Package: audiofiles
43 + Version: ${VERSION}
44 + Section: sound
45 + Priority: optional
46 + Architecture: ${ARCH}
47 + Depends: libasound2, libgl1, libxcb1, libxkbcommon0, libssl3, libgtk-3-0
48 + Installed-Size: ${INSTALLED_SIZE}
49 + Maintainer: Maxwell Morgan Johnson <max@makenot.work>
50 + Homepage: https://makenot.work
51 + Description: Sample manager with content-addressed storage
52 + AudioFiles is a desktop sample manager with content-addressed storage,
53 + virtual filesystem, and cloud sync. Supports WAV, FLAC, MP3, OGG, and
54 + AIFF formats.
55 + CONTROL
56 +
57 + # Step 3: Set permissions
58 + chmod 755 "$PKG_DIR/usr/bin/audiofiles-app"
59 + chmod 644 "$PKG_DIR/usr/share/applications/audiofiles.desktop"
60 + chmod 644 "$PKG_DIR/usr/share/icons/hicolor/256x256/apps/audiofiles.png"
61 +
62 + # Step 4: Build .deb
63 + echo "==> Building .deb..."
64 + DEB_NAME="audiofiles_${VERSION}_${ARCH}.deb"
65 + DEB_PATH="$DIST_DIR/$DEB_NAME"
66 + rm -f "$DEB_PATH"
67 +
68 + dpkg-deb --build --root-owner-group "$PKG_DIR" "$DEB_PATH"
69 +
70 + # Cleanup
71 + rm -rf "$DIST_DIR/.deb-staging"
72 +
73 + echo ""
74 + echo "Done: $DEB_PATH"
75 + echo "Install with: sudo dpkg -i $DEB_NAME"
@@ -0,0 +1,142 @@
1 + #!/usr/bin/env bash
2 + set -euo pipefail
3 +
4 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5 + PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6 + DIST_DIR="$SCRIPT_DIR"
7 +
8 + # Parse flags
9 + SKIP_NOTARIZE=false
10 + for arg in "$@"; do
11 + case "$arg" in
12 + --skip-notarize) SKIP_NOTARIZE=true ;;
13 + *) echo "Unknown flag: $arg"; echo "Usage: $0 [--skip-notarize]"; exit 1 ;;
14 + esac
15 + done
16 +
17 + # Read version from Cargo.toml
18 + VERSION=$(grep '^version' "$PROJECT_DIR/crates/audiofiles-app/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
19 + echo "Building AudioFiles v${VERSION}"
20 +
21 + ARCH=$(uname -m)
22 + APP_BUNDLE="$DIST_DIR/AudioFiles.app"
23 + DMG_NAME="AudioFiles_${VERSION}_${ARCH}.dmg"
24 + DMG_PATH="$DIST_DIR/$DMG_NAME"
25 + SIGNING_IDENTITY="Developer ID Application: MAXWELL MORGAN JOHNSON (93C54W92UP)"
26 + NOTARY_PROFILE="notarytool-profile"
27 +
28 + # Step 1: Build release binary
29 + echo "==> Building release binary..."
30 + cd "$PROJECT_DIR"
31 + cargo build --release -p audiofiles-app
32 +
33 + # Step 2: Assemble app bundle
34 + echo "==> Assembling app bundle..."
35 + rm -rf "$APP_BUNDLE"
36 + mkdir -p "$APP_BUNDLE/Contents/MacOS"
37 + mkdir -p "$APP_BUNDLE/Contents/Resources"
38 +
39 + cp "$PROJECT_DIR/target/release/audiofiles-app" "$APP_BUNDLE/Contents/MacOS/audiofiles-app"
40 + cp "$PROJECT_DIR/crates/audiofiles-app/AppIcon.icns" "$APP_BUNDLE/Contents/Resources/AppIcon.icns"
41 +
42 + # Patch Info.plist with current version
43 + sed -e "s|<string>0\.[0-9]*\.[0-9]*</string><!-- CFBundleVersion -->|<string>${VERSION}</string><!-- CFBundleVersion -->|" \
44 + "$DIST_DIR/Info.plist.template" > "$APP_BUNDLE/Contents/Info.plist" 2>/dev/null || true
45 +
46 + # If template doesn't exist, write Info.plist directly with current version
47 + if [ ! -f "$APP_BUNDLE/Contents/Info.plist" ] || [ ! -s "$APP_BUNDLE/Contents/Info.plist" ]; then
48 + cat > "$APP_BUNDLE/Contents/Info.plist" << PLIST
49 + <?xml version="1.0" encoding="UTF-8"?>
50 + <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
51 + <plist version="1.0">
52 + <dict>
53 + <key>CFBundleName</key>
54 + <string>AudioFiles</string>
55 + <key>CFBundleIdentifier</key>
56 + <string>com.audiofiles.app</string>
57 + <key>CFBundleVersion</key>
58 + <string>${VERSION}</string>
59 + <key>CFBundleShortVersionString</key>
60 + <string>${VERSION}</string>
61 + <key>CFBundleExecutable</key>
62 + <string>audiofiles-app</string>
63 + <key>CFBundleIconFile</key>
64 + <string>AppIcon</string>
65 + <key>CFBundlePackageType</key>
66 + <string>APPL</string>
67 + <key>NSHighResolutionCapable</key>
68 + <true/>
69 + <key>CFBundleDocumentTypes</key>
70 + <array>
71 + <dict>
72 + <key>CFBundleTypeName</key>
73 + <string>Audio File</string>
74 + <key>CFBundleTypeRole</key>
75 + <string>Viewer</string>
76 + <key>LSHandlerRank</key>
77 + <string>Alternate</string>
78 + <key>CFBundleTypeExtensions</key>
79 + <array>
80 + <string>wav</string>
81 + <string>flac</string>
82 + <string>mp3</string>
83 + <string>ogg</string>
84 + <string>aiff</string>
85 + <string>aif</string>
86 + </array>
87 + </dict>
88 + </array>
89 + </dict>
90 + </plist>
91 + PLIST
92 + fi
93 +
94 + # Step 3: Code sign the app bundle
95 + echo "==> Signing app bundle..."
96 + codesign --deep --force --options runtime \
97 + --sign "$SIGNING_IDENTITY" \
98 + "$APP_BUNDLE"
99 +
100 + echo "==> Verifying signature..."
101 + codesign --verify --deep --strict "$APP_BUNDLE"
102 +
103 + # Step 4: Create DMG
104 + echo "==> Creating DMG..."
105 + rm -f "$DMG_PATH"
106 +
107 + DMG_STAGING="$DIST_DIR/.dmg-staging"
108 + rm -rf "$DMG_STAGING"
109 + mkdir -p "$DMG_STAGING"
110 + cp -R "$APP_BUNDLE" "$DMG_STAGING/"
111 + ln -s /Applications "$DMG_STAGING/Applications"
112 +
113 + hdiutil create -volname "AudioFiles" \
114 + -srcfolder "$DMG_STAGING" \
115 + -ov -format UDZO \
116 + "$DMG_PATH"
117 +
118 + rm -rf "$DMG_STAGING"
119 +
120 + # Step 5: Sign the DMG
121 + echo "==> Signing DMG..."
122 + codesign --force --sign "$SIGNING_IDENTITY" "$DMG_PATH"
123 +
124 + if [ "$SKIP_NOTARIZE" = true ]; then
125 + echo "==> Skipping notarization (--skip-notarize)"
126 + echo ""
127 + echo "Done: $DMG_PATH"
128 + exit 0
129 + fi
130 +
131 + # Step 6: Notarize
132 + echo "==> Notarizing DMG..."
133 + xcrun notarytool submit "$DMG_PATH" \
134 + --keychain-profile "$NOTARY_PROFILE" \
135 + --wait
136 +
137 + # Step 7: Staple
138 + echo "==> Stapling notarization ticket..."
139 + xcrun stapler staple "$DMG_PATH"
140 +
141 + echo ""
142 + echo "Done: $DMG_PATH"
@@ -0,0 +1,44 @@
1 + #!/usr/bin/env bash
2 + set -euo pipefail
3 +
4 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5 + PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
6 + DIST_DIR="$SCRIPT_DIR"
7 + TARGET="x86_64-pc-windows-msvc"
8 +
9 + # Read version from Cargo.toml
10 + VERSION=$(grep '^version' "$PROJECT_DIR/crates/audiofiles-app/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
11 + echo "Building AudioFiles v${VERSION} Windows (${TARGET})"
12 +
13 + # Prerequisites check
14 + if ! command -v cargo-xwin &>/dev/null; then
15 + echo "Error: cargo-xwin not found. Install with: cargo install cargo-xwin"
16 + exit 1
17 + fi
18 +
19 + if ! rustup target list --installed | grep -q "$TARGET"; then
20 + echo "Error: Rust target $TARGET not installed. Add with: rustup target add $TARGET"
21 + exit 1
22 + fi
23 +
24 + # Step 1: Build release binary
25 + echo "==> Building release binary..."
26 + cd "$PROJECT_DIR"
27 + cargo xwin build --release -p audiofiles-app --target "$TARGET"
28 +
29 + # Step 2: Copy artifact
30 + echo "==> Copying artifact..."
31 + EXE_NAME="audiofiles-app.exe"
32 + SRC="$PROJECT_DIR/target/$TARGET/release/$EXE_NAME"
33 +
34 + if [ ! -f "$SRC" ]; then
35 + echo "Error: Build artifact not found at $SRC"
36 + exit 1
37 + fi
38 +
39 + DEST="$DIST_DIR/AudioFiles_${VERSION}_x86_64.exe"
40 + cp "$SRC" "$DEST"
41 +
42 + echo ""
43 + echo "Done: $DEST"
44 + echo "Size: $(du -h "$DEST" | cut -f1)"