From a7f9b6e4fb61e65a393369c71bcedef68b229834 Mon Sep 17 00:00:00 2001 From: Echo Date: Fri, 31 Jul 2026 09:16:35 -0400 Subject: [PATCH] feat: override app path --- README.md | 12 ++++++++++++ install.sh | 24 +++++++++++++++++++----- scripts/byoe/build-handoff-app.js | 23 ++++++++++++++++------- scripts/byoe/inject.js | 2 +- scripts/byoe/slack-updater.js | 16 +++++++--------- 5 files changed, 55 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index c2dae21..c66aab2 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,18 @@ Install the official [Slack app](https://slack.com/downloads/mac) (not the App S curl -fsSL https://raw.githubusercontent.com/3kh0/slick/main/install.sh | bash ``` +If Slack is installed somewhere else, pass its app bundle with `--slack-app`: + +```bash +./install.sh --slack-app "$HOME/Documents/Apps/Slack.app" +``` + +When using the remote installer, pass the option to `bash` like this: + +```bash +curl -fsSL https://raw.githubusercontent.com/3kh0/slick/main/install.sh | bash -s -- --slack-app "$HOME/Documents/Apps/Slack.app" +``` + If you prefer doing it by hand, grab the latest prebuilt app from the [releases page](https://github.com/3kh0/slick/releases/latest) and pick the build for your Mac (check > About This Mac > Chip if unsure): - `Slick-build-N-mac-arm64` — **Apple Silicon** (if there is a M in the name) diff --git a/install.sh b/install.sh index a169f9d..6450ac8 100755 --- a/install.sh +++ b/install.sh @@ -43,15 +43,29 @@ exit(LSSetDefaultHandlerForURLScheme("slack" as NSString as CFString, id as NSSt EOF } -if [ "${1:-}" = "--restore-handler" ]; then - handler com.tinyspeck.slackmacgap && echo "slack:// now opens the official Slack again." || die "could not restore handler" - exit 0 -fi +while [ "$#" -gt 0 ]; do + case "$1" in + --slack-app) + [ "$#" -ge 2 ] || die "--slack-app needs a path" + SLACK="${2%/}" + shift 2 + ;; + --restore-handler) + handler com.tinyspeck.slackmacgap && echo "slack:// now opens the official Slack again." || die "could not restore handler" + exit 0 + ;; + *) die "unknown option: $1" ;; + esac +done step "Checking prerequisites" [ "$(uname -s)" = "Darwin" ] || die "Slick only supports macOS :(" [ -f "$SLACK/Contents/Resources/app.asar" ] \ || die "Slack not found at $SLACK, please install it from slack.com first." +SLACK="$(cd "$(dirname "$SLACK")" && pwd)/$(basename "$SLACK")" +SLACK_CONFIG="$HOME/Library/Application Support/Slick/slick/slack-app-path" +mkdir -p "$(dirname "$SLACK_CONFIG")" +printf '%s\n' "$SLACK" > "$SLACK_CONFIG" if [ -f "$ROOT/scripts/byoe/build-handoff-app.js" ]; then node -e 'process.exit(parseInt(process.versions.node, 10) >= 18 ? 0 : 1)' 2>/dev/null \ @@ -99,7 +113,7 @@ if [ -f "$ROOT/scripts/byoe/build-handoff-app.js" ]; then step "Building $APP (Build $BUILD)" node "$ROOT/scripts/byoe/build-handoff-app.js" --target "$APP" \ --profile "$HOME/Library/Application Support/Slack" \ - --app-version "$VERSION" --build-number "$BUILD" --allow-non-tmp --force >/dev/null + --slack-app "$SLACK" --app-version "$VERSION" --build-number "$BUILD" --allow-non-tmp --force >/dev/null step "Installing icon" "$ROOT/scripts/byoe/set-icon.sh" 2>&1 | while IFS= read -r line; do printf ' %s\n' "$line"; done diff --git a/scripts/byoe/build-handoff-app.js b/scripts/byoe/build-handoff-app.js index d7b3042..eb3e44d 100644 --- a/scripts/byoe/build-handoff-app.js +++ b/scripts/byoe/build-handoff-app.js @@ -7,14 +7,13 @@ const { spawnSync } = require('child_process'); const ROOT = path.resolve(__dirname, '..', '..'); const DEFAULT_SOURCE_APP = path.join(ROOT, 'byoe/node_modules/electron/dist/Electron.app'); -const SLACK_RESOURCES = '/Applications/Slack.app/Contents/Resources'; -const SLACK_ASAR = path.join(SLACK_RESOURCES, 'app.asar'); const ENTITLEMENTS = path.join(ROOT, 'scripts/release/entitlements.plist'); const DEFAULTS = { target: '/tmp/slick/Slick.app', profile: '/tmp/slick/profile', appVersion: '1.0.0', buildNumber: '0', + slackApp: '/Applications/Slack.app', sourceApp: process.env.SLICK_SOURCE_APP || DEFAULT_SOURCE_APP, force: false, allowNonTmp: false, @@ -24,6 +23,7 @@ function usage() { console.error(`Usage: node scripts/byoe/build-handoff-app.js [--target ] [--profile ] [--app-version ] [--build-number ] + [--slack-app ] [--source-app ] [--force] [--allow-non-tmp] Defaults: @@ -31,6 +31,7 @@ Defaults: --profile ${DEFAULTS.profile} --app-version ${DEFAULTS.appVersion} --build-number ${DEFAULTS.buildNumber} + --slack-app ${DEFAULTS.slackApp} --source-app ${DEFAULTS.sourceApp}`); process.exit(2); } @@ -42,6 +43,7 @@ function parseArgs(argv) { else if (argv[i] === '--profile') o.profile = argv[++i] || usage(); else if (argv[i] === '--app-version') o.appVersion = argv[++i] || usage(); else if (argv[i] === '--build-number') o.buildNumber = argv[++i] || usage(); + else if (argv[i] === '--slack-app') o.slackApp = argv[++i] || usage(); else if (argv[i] === '--source-app') o.sourceApp = argv[++i] || usage(); else if (argv[i] === '--force') o.force = true; else if (argv[i] === '--allow-non-tmp') o.allowNonTmp = true; @@ -179,17 +181,23 @@ const { app, dialog, shell, Menu, MenuItem } = require('electron'); const SLICK_ROOT = path.join(process.resourcesPath, 'slick'); const PROFILE = process.env.SLICK_HANDOFF_PROFILE || path.join(app.getPath('appData'), 'Slick'); const DEFAULT_THEME = ${JSON.stringify(defaultTheme)}; -const SLACK_RESOURCES = ${JSON.stringify(SLACK_RESOURCES)}; -const SLACK_ASAR = ${JSON.stringify(SLACK_ASAR)}; +const DEFAULT_SLACK_APP = ${JSON.stringify(path.resolve(opts.slackApp))}; +const SLACK_PATH_CONFIG = path.join(PROFILE, 'slick', 'slack-app-path'); +let SLACK_APP = DEFAULT_SLACK_APP; +try { + SLACK_APP = fs.readFileSync(SLACK_PATH_CONFIG, 'utf8').trim() || DEFAULT_SLACK_APP; +} catch {} +const SLACK_RESOURCES = path.join(SLACK_APP, 'Contents', 'Resources'); +const SLACK_ASAR = path.join(SLACK_RESOURCES, 'app.asar'); const SLICK_VERSION = ${JSON.stringify(opts.appVersion)}; const SLICK_BUILD = parseInt(${JSON.stringify(opts.buildNumber)}, 10) || 0; const updater = require(path.join(SLICK_ROOT, 'scripts/byoe/updater.js')).create({ version: SLICK_VERSION, build: SLICK_BUILD, profile: PROFILE }); const RELEASES_URL = updater.RELEASES_URL; -const slackUpdater = require(path.join(SLICK_ROOT, 'scripts/byoe/slack-updater.js')).create({ version: SLICK_VERSION, profile: PROFILE }); +const slackUpdater = require(path.join(SLICK_ROOT, 'scripts/byoe/slack-updater.js')).create({ version: SLICK_VERSION, profile: PROFILE, slackApp: SLACK_APP }); function slackElectronMajor() { try { - const plist = '/Applications/Slack.app/Contents/Frameworks/Electron Framework.framework/Resources/Info.plist'; + const plist = path.join(SLACK_APP, 'Contents/Frameworks/Electron Framework.framework/Resources/Info.plist'); const raw = require('child_process').execFileSync( '/usr/bin/plutil', ['-extract', 'CFBundleVersion', 'raw', '-o', '-', plist], { encoding: 'utf8' }, ); @@ -214,7 +222,7 @@ function handlePreflight(problem) { type: 'error', title: 'Slick', message: 'Slack is not installed', - detail: 'Slick needs the official Slack app at /Applications/Slack.app. Install it from slack.com, then open Slick again.', + detail: 'Slick needs the official Slack app at ' + SLACK_APP + '. Install it there or reinstall Slick with --slack-app, then open Slick again.', buttons: ['Quit'], }); return false; @@ -359,6 +367,7 @@ try { function boot() { app.setPath('userData', PROFILE); + process.env.SLICK_SLACK_APP = SLACK_APP; installPatch(); seedSettings(); updater.scheduleUpdateChecks(); diff --git a/scripts/byoe/inject.js b/scripts/byoe/inject.js index 06fcc48..c042c8c 100644 --- a/scripts/byoe/inject.js +++ b/scripts/byoe/inject.js @@ -388,7 +388,7 @@ app.on('session-created', ap); function installNotificationSounds() { if (process.platform !== 'darwin') return; - const R = '/Applications/Slack.app/Contents/Resources'; + const R = path.join(process.env.SLICK_SLACK_APP || '/Applications/Slack.app', 'Contents', 'Resources'); const s = path.join(app.getPath('home'), 'Library', 'Sounds'); let names; try { diff --git a/scripts/byoe/slack-updater.js b/scripts/byoe/slack-updater.js index 0fd8d63..b5e7de0 100644 --- a/scripts/byoe/slack-updater.js +++ b/scripts/byoe/slack-updater.js @@ -26,8 +26,6 @@ const https = require('https'); const { execFile, execFileSync } = require('child_process'); const MAC = process.platform === 'darwin'; -const SLACK_APP = '/Applications/Slack.app'; -const SLACK_INFO_PLIST = path.join(SLACK_APP, 'Contents/Info.plist'); const FRAMEWORK_PLIST_REL = 'Contents/Frameworks/Electron Framework.framework/Resources/Info.plist'; const SLACK_BUNDLE_ID = 'com.tinyspeck.slackmacgap'; const LATEST_REDIRECT = 'https://slack.com/ssb/download-osx-universal'; @@ -61,8 +59,6 @@ function plistValue(plist, key) { } const electronMajorOf = (app) => parseInt(plistValue(path.join(app, FRAMEWORK_PLIST_REL), 'CFBundleVersion'), 10) || 0; const bundleIdOf = (app) => plistValue(path.join(app, 'Contents/Info.plist'), 'CFBundleIdentifier'); -const installedVersion = () => plistValue(SLACK_INFO_PLIST, 'CFBundleShortVersionString'); - // Move a bundle dir onto another path: cheap rename within a volume, ditto copy across. function moveDir(from, to) { try { @@ -72,7 +68,9 @@ function moveDir(from, to) { } } -function create({ profile, version }) { +function create({ profile, version, slackApp = '/Applications/Slack.app' }) { + const slackInfoPlist = path.join(slackApp, 'Contents/Info.plist'); + const installedVersion = () => plistValue(slackInfoPlist, 'CFBundleShortVersionString'); if (!MAC) { const noop = () => {}; return { @@ -168,17 +166,17 @@ function create({ profile, version }) { clearStaging() ); - const backup = `${SLACK_APP}.slick-old`; + const backup = `${slackApp}.slick-old`; try { fs.rmSync(backup, { recursive: true, force: true }); - if (fs.existsSync(SLACK_APP)) fs.renameSync(SLACK_APP, backup); - moveDir(stagedApp, SLACK_APP); + if (fs.existsSync(slackApp)) fs.renameSync(slackApp, backup); + moveDir(stagedApp, slackApp); fs.rmSync(backup, { recursive: true, force: true }); log(`installed Slack ${marker.version}`); } catch (e) { // Restore whatever we moved so the user is never left without Slack. try { - if (!fs.existsSync(SLACK_APP) && fs.existsSync(backup)) fs.renameSync(backup, SLACK_APP); + if (!fs.existsSync(slackApp) && fs.existsSync(backup)) fs.renameSync(backup, slackApp); } catch {} log(`failed to install staged Slack: ${msg(e)}`); } finally {