Coder Social home page Coder Social logo

hraban / mac-app-util Goto Github PK

View Code? Open in Web Editor NEW
103.0 4.0 3.0 50 KB

Fix .app programs installed by Nix on Mac

License: GNU Affero General Public License v3.0

Nix 25.58% Common Lisp 74.35% Shell 0.07%
apple macos plist plist-files plutil common-lisp flakes nix

mac-app-util's Introduction

(Nix) Utilities for Mac App launchers

How to use in Nix

Use the home-manager module to “fix” your .app launchers:

  • Pinning in Dock works across updates
  • Launch from Spotlight
  • Create .app wrappers for non-app bundle, stand-alone binary programs

Now you can launch Nix-installed apps using only your keyboard, using ⌘ space.

Flakes

How to use this in nix-darwin or home-manager, assuming you use flakes:

{
  inputs = {
    #...
    mac-app-util.url = "github:hraban/mac-app-util";
    #...
  };

  outputs = {
    nix-darwin
    , home-manager
    , mac-app-util
    , ...
  }: {
    darwinConfigurations = {
      MyHost = nix-darwin.lib.darwinSystem {
        # ...

        modules = [
          mac-app-util.darwinModules.default

          # And if you also use home manager:
          home-manager.darwinModules.home-manager
          (
            { pkgs, config, inputs, ... }:
            {
              # To enable it for all users:
              home-manager.sharedModules = [
                mac-app-util.homeManagerModules.default
              ];

              # Or to enable it for a single user only:
              home-manager.users.foobar.imports = [
                #...
                mac-app-util.homeManagerModules.default
              ];
            }
          )

        ];
      };
    };
  };
}

Non-flakes

Since I use flakes for home manager and nix-darwin I’m not 100% on how to do this, but I suspect that it’s similar to the above, at its core.

What will be different is the “plumbing”, i.e. how to get a reference to this app’s derivation. Here’s how:

let
  mac-app-util-src = pkgs.fetchFromGitHub {
    repo = "mac-app-util";
    owner = "hraban";
    # Replace these two lines by the corresponding entries from:
    # $ nix run --experimental-features 'nix-command flakes' nixpkgs#nix-prefetch-github -- hraban mac-app-util
    rev = "";
    hash = "";
  };
  mac-app-util = (pkgs.callPackage mac-app-util-src {});
in

# Now you have either the program as a derivation itself:
mac-app-util.default

# Or the home manager module:
mac-app-util.homeManagerModules.default

# Or darwin:
mac-app-util.darwinModules.default

The rest is up to you. Good luck!

Commands

At the core of this project is a (Nix-agnostic) program that can:

mktrampoline
Create a “trampoline” launcher app
sync-dock
Update persistent apps in the Dock
sync-trampolines
Create a directory with trampolines to all your apps

mktrampoline

This creates a “trampoline” launcher app which is a simple wrapper application that just launches your actual application.

$ nix run github:hraban/mac-app-util -- mktrampoline /path/to/MyApp.app /Applications/MyApp.app

Intuitively, you would either fully copy & paste the original .app, or create a symlink or “alias”; all of those solutions have different problems and they don’t get indexed by Spotlight properly.

This trampoline script is indexed by Spotlight and by Launchpad, so you can keep launching your apps using ⌘ SPC <appname> ⏎

You can also wrap non-app stand-alone binaries with this. For example:

$ nix run github:hraban/mac-app-util -- mktrampoline "$(which darktable)" ~/Applications/Darktable.app

Darktable is a photo editor available on Mac but without a .app bundle in the derivation. It’s just a stand-alone binary. Using mktrampoline, you can make it launchable from Spotlight.

See nix-community/home-manager#1341

sync-dock

When you have an app in your Dock which doesn’t live in /Applications/.., it can get stale: e.g. your app at /foo/v1/Foo.app gets replaced by /foo/v2/Foo.app. To automatically update the Dock to the new location of Foo, execute:

$ nix run github:hraban/mac-app-util -- sync-dock Foo.app

It will find an old persistent item by the name of “Foo” and update it to the new location.

N.B.: This is currently limited only to Nix apps, but actually it could work for anything. I’ve just kept it conservative to be on the safe side.

sync-trampolines

Combines mktrampoline and sync-dock to create a fresh directory with a fresh trampoline for every source app. E.g.:

$ nix run github:hraban/mac-app-util -- sync-trampolines ~/special/apps/ ~/Applications/Special/

Will create a fresh directory (~/Applications/Special), deleting if it already existed. In that directory it will create a trampoline app for every single *.app file it finds in ~/special/apps/.

This helps register apps from outside of your ~/Applications directory with Spotlight and the Launchpad.

License

mac-app-util - Manage Mac App launchers Copyright © 2023–2024 Hraban Luyat

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

mac-app-util's People

Contributors

hraban avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

mac-app-util's Issues

Script fails due to missing `jq` binary

Just tried this out and was faced with the following error:

;;; Computing Hangul syllable names.: replacing existing signature
/bin/sh: jq: command not found
Unhandled UIOP/RUN-PROGRAM:SUBPROCESS-ERROR in thread #<SB-THREAD:THREAD "main thread" RUNNING
                                                         {7008820403}>:
  Subprocess with command "(/usr/bin/plutil -convert json -- orig && /usr/bin/plutil -convert json -- bare-wrapper && jq --argjson keys \"[\\\"CFBundleDevelopmentRegion\\\",\\\"CFBundleDocumentTypes\\\",\\\"CFBundleGetInfoString\\\",\\\"CFBundleIconFile\\\",\\\"CFBundleIdentifier\\\",\\\"CFBundleInfoDictionaryVersion\\\",\\\"CFBundleName\\\",\\\"CFBundleShortVersionString\\\",\\\"CFBundleURLTypes\\\",\\\"NSAppleEventsUsageDescription\\\",\\\"NSAppleScriptEnabled\\\",\\\"NSDesktopFolderUsageDescription\\\",\\\"NSDocumentsFolderUsageDescription\\\",\\\"NSDownloadsFolderUsageDescription\\\",\\\"NSPrincipalClass\\\",\\\"NSRemovableVolumesUsageDescription\\\",\\\"NSServices\\\",\\\"UTExportedTypeDeclarations\\\"]\" \"to_entries |[.[]| select(.key as \\$item| \\$keys | index(\\$item) >= 0) ] | from_entries\" < orig > filtered && (cat bare-wrapper filtered | jq -s add > final) && /usr/bin/plutil -convert xml1 -- final)"
 exited with error code 127

Backtrace for: #<SB-THREAD:THREAD "main thread" RUNNING {7008820403}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK SB-EXT:*INVOKE-DEBUGGER-HOOK* #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}>)
2: (INVOKE-DEBUGGER #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}>)
3: (ERROR #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}>)
4: ((LAMBDA (SH::C) :IN SH:RUN/NIL) #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}>)
5: (SB-KERNEL::%SIGNAL #<UIOP/RUN-PROGRAM:SUBPROCESS-ERROR {7005ED82D3}>)
6: (CERROR "IGNORE-ERROR-STATUS" UIOP/RUN-PROGRAM:SUBPROCESS-ERROR :COMMAND #<(SIMPLE-ARRAY CHARACTER (819)) (/usr/bin/plutil -convert json -- orig && /usr/bin/plutil -convert json -- bare-wrapper && jq --argjson keys "[\"CFBundleDevelopmentRegion\",\"CFBundleDocumentTypes\",\"CFBundleGetInfoString\",\"CFBun... {7005EC3E8F}> :CODE 127 :PROCESS NIL)
7: (UIOP/RUN-PROGRAM::%CHECK-RESULT 127 :COMMAND #<(SIMPLE-ARRAY CHARACTER (819)) (/usr/bin/plutil -convert json -- orig && /usr/bin/plutil -convert json -- bare-wrapper && jq --argjson keys "[\"CFBundleDevelopmentRegion\",\"CFBundleDocumentTypes\",\"CFBundleGetInfoString\",\"CFBun... {7005EC3E8F}> :PROCESS NIL :IGNORE-ERROR-STATUS NIL)
8: (UIOP/RUN-PROGRAM::%USE-SYSTEM #<(SIMPLE-ARRAY CHARACTER (819)) (/usr/bin/plutil -convert json -- orig && /usr/bin/plutil -convert json -- bare-wrapper && jq --argjson keys "[\"CFBundleDevelopmentRegion\",\"CFBundleDocumentTypes\",\"CFBundleGetInfoString\",\"CFBun... {7005EC3E8F}> :IGNORE-ERROR-STATUS NIL :HOST NIL :OUTPUT T :ERROR-OUTPUT T :SHOW NIL)
9: ((FLET SH::RUN-IT :IN SH:RUN/NIL))
10: ((FLET "F1" :IN COPY-PATHS))
11: (COPY-PATHS "/nix/store/n1rs0c0xm3mfyn234ylq93lyhl3wn28f-terminal-notifier-2.0.0/Applications/terminal-notifier.app//Contents/Info.plist" "/Applications/Nix Trampolines/terminal-notifier.app/Contents/Info.plist" #<unused argument>)
12: (SYNC-TRAMPOLINES "/Applications/Nix Apps" "/Applications/Nix Trampolines")
13: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MAIN) #<NULL-LEXENV>)
14: (SB-EXT:EVAL-TLF (MAIN) 38 NIL)
15: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (MAIN) 38)
16: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) (MAIN) :CURRENT-INDEX 38)
17: (SB-C::%DO-FORMS-FROM-INFO #<FUNCTION (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {70088303EB}> #<SB-C::SOURCE-INFO {7008830423}> SB-C::INPUT-ERROR-IN-LOAD)
18: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
19: ((LABELS SB-FASL::LOAD-STREAM-1 :IN LOAD) #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}> NIL)
20: (SB-FASL::CALL-WITH-LOAD-BINDINGS #<FUNCTION (LABELS SB-FASL::LOAD-STREAM-1 :IN LOAD) {104FD09EB}> #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}> NIL #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}>)
21: (LOAD #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST :ERROR :EXTERNAL-FORMAT :DEFAULT)
22: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped" {7008830003}>)
23: ((FLET SB-UNIX::BODY :IN SB-IMPL::PROCESS-SCRIPT))
24: ((FLET "WITHOUT-INTERRUPTS-BODY-11" :IN SB-IMPL::PROCESS-SCRIPT))
25: (SB-IMPL::PROCESS-SCRIPT "/nix/store/mr1kr6995i4r38gc1m52q5789bqqyxnc-mac-app-util/bin/.mac-app-util-wrapped")
26: (SB-IMPL::TOPLEVEL-INIT)
27: ((FLET SB-UNIX::BODY :IN SB-IMPL::START-LISP))
28: ((FLET "WITHOUT-INTERRUPTS-BODY-3" :IN SB-IMPL::START-LISP))
29: (SB-IMPL::%START-LISP)

unhandled condition in --disable-debugger mode, quitting

Seems like the CL program expects jq to be installed as a system dependency, but I think it would be more correct to make it available by way of the nix environment, rather than expecting it to be ambiently available.

Curiously, I do have jq in my user PATH, but it's installed by nix, so I wonder if that makes it hard for the CL program to find.

Exception raised when the directory doesn't have the extension in lowercase

The following exception is incorrectly raised on mktrampoline with apps that uses a extension different from .app, like .App, used by Vesktop.App or Electron.App:

Unhandled SIMPLE-ERROR in thread #<SB-THREAD:THREAD tid=259 "main thread" RUNNING
                                    {7004AB0143}>:
  Directory /nix/store/ir8vv5d25qfclz59pm13salwlkn2j5qg-vesktop-1.5.2/Applications/Vesktop.App/ does not end in ‘.app’ is this a Mac app?

Backtrace for: #<SB-THREAD:THREAD tid=259 "main thread" RUNNING {7004AB0143}>
0: (SB-DEBUG::DEBUGGER-DISABLED-HOOK #<SIMPLE-ERROR "Directory ~A does not end in ‘.app’ is this a Mac app?" {7005F49623}> #<unused argument> :QUIT T)
1: (SB-DEBUG::RUN-HOOK SB-EXT:*INVOKE-DEBUGGER-HOOK* #<SIMPLE-ERROR "Directory ~A does not end in ‘.app’ is this a Mac app?" {7005F49623}>)
2: (INVOKE-DEBUGGER #<SIMPLE-ERROR "Directory ~A does not end in ‘.app’ is this a Mac app?" {7005F49623}>)
3: (ERROR "Directory ~A does not end in ‘.app’ is this a Mac app?" #P"/nix/store/ir8vv5d25qfclz59pm13salwlkn2j5qg-vesktop-1.5.2/Applications/Vesktop.App/")
4: ((:METHOD MKTRAMPOLINE (PATHNAME PATHNAME)) #P"/nix/store/ir8vv5d25qfclz59pm13salwlkn2j5qg-vesktop-1.5.2/Applications/Vesktop.App/" #P"/Users/kutu/Applications/Home Manager Trampolines/Vesktop.app") [fast-method]
5: (SB-INT:SIMPLE-EVAL-IN-LEXENV (MAIN) #<NULL-LEXENV>)
6: (SB-EXT:EVAL-TLF (MAIN) 45 NIL)
7: ((LABELS SB-FASL::EVAL-FORM :IN SB-INT:LOAD-AS-SOURCE) (MAIN) 45)
8: ((LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) (MAIN) :CURRENT-INDEX 45)
9: (SB-C::%DO-FORMS-FROM-INFO #<FUNCTION (LAMBDA (SB-KERNEL:FORM &KEY :CURRENT-INDEX &ALLOW-OTHER-KEYS) :IN SB-INT:LOAD-AS-SOURCE) {105320E1B}> #<SB-C::SOURCE-INFO {7004B17D93}> SB-C::INPUT-ERROR-IN-LOAD)
10: (SB-INT:LOAD-AS-SOURCE #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}> :VERBOSE NIL :PRINT NIL :CONTEXT "loading")
11: ((LABELS SB-FASL::LOAD-STREAM-1 :IN LOAD) #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}> NIL)
12: (SB-FASL::CALL-WITH-LOAD-BINDINGS #<FUNCTION (LABELS SB-FASL::LOAD-STREAM-1 :IN LOAD) {1053209EB}> #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}> NIL #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}>)
13: (LOAD #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}> :VERBOSE NIL :PRINT NIL :IF-DOES-NOT-EXIST :ERROR :EXTERNAL-FORMAT :DEFAULT)
14: ((FLET SB-IMPL::LOAD-SCRIPT :IN SB-IMPL::PROCESS-SCRIPT) #<SB-SYS:FD-STREAM for "file /nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped" {7004AB1B93}>)
15: ((FLET SB-UNIX::BODY :IN SB-IMPL::PROCESS-SCRIPT))
16: ((FLET "WITHOUT-INTERRUPTS-BODY-" :IN SB-IMPL::PROCESS-SCRIPT))
17: (SB-IMPL::PROCESS-SCRIPT "/nix/store/4f0sak3nw30da020sp6fm867azx48cj7-mac-app-util/bin/.mac-app-util-wrapped")
18: (SB-IMPL::TOPLEVEL-INIT)
19: ((FLET SB-UNIX::BODY :IN SB-IMPL::START-LISP))
20: ((FLET "WITHOUT-INTERRUPTS-BODY-3" :IN SB-IMPL::START-LISP))
21: (SB-IMPL::%START-LISP)

unhandled condition in --disable-debugger mode, quitting

Raised by the code:

(if (str:ends-with-p ".app" (first (last (pathname-directory from))))

Maybe instead of validating with the path of the app is would be better to check if Contents/Info.plist exists.

Noop-Module on Linux and/or enable option

Thanks for the module, it works great!

On my hosts (Linux/Darwin) i use Home Manager as a standalone installation and reuse the same home.nix file and HM modules.
Since conditional module imports such as

  imports = [
    ./modules
  ] ++ lib.lists.optional pkgs.stdenv.isDarwin inputs.mac-app-util.homeManagerModules.default;

are not possible, it would be great if this module could simply be imported on Linux systems aswell and be a noop module in that case. Alternatively an enable option would be great.

Currently the assertions prevent an import on linux systems, which requires me to define separate "entrypoint" home.nix files on my darwin based systems.

Question on usage

Hello,

I am interested in using this home-manager module with the following use case:

  • Use Home Manager to manage "global packages"
  • Install Mac OS apps, like Emacs, and have it automatically appear in ~/Applications, Spotlight, etc.

I have set up home-manager with this module. Are there any extra steps one has to do after e.g. updating Emacs? Or does this get handled automatically when running nix flake update && home-manager switch ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.