Repository: lra/mackup Branch: master Commit: 25961212bbcd Files: 673 Total size: 333.2 KB Directory structure: gitextract_y1tjtpic/ ├── .github/ │ ├── CONTRIBUTING.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── install.yaml │ ├── linelint.yaml │ ├── markdown.yaml │ ├── mypy.yaml │ ├── ruff.yaml │ ├── snap.yaml │ └── test.yaml ├── .gitignore ├── .linelint.yml ├── .markdownlint.yaml ├── .markdownlintignore ├── INSTALL.md ├── LICENSE ├── Makefile ├── README.md ├── doc/ │ ├── .mackup/ │ │ ├── gnupg.cfg │ │ ├── mackup.cfg │ │ ├── my-own-files.cfg │ │ └── ssh.cfg │ ├── .mackup.cfg │ ├── ARCHITECTURE.md │ ├── README.md │ ├── configuration_merge_guide.md │ ├── develop.md │ ├── release.md │ └── syncing-private-keys.md ├── pyproject.toml ├── snap/ │ └── snapcraft.yaml ├── src/ │ └── mackup/ │ ├── __init__.py │ ├── application.py │ ├── applications/ │ │ ├── 1password-4.cfg │ │ ├── 2do.cfg │ │ ├── ack.cfg │ │ ├── act.cfg │ │ ├── activitywatch.cfg │ │ ├── adium.cfg │ │ ├── adobe-camera-raw.cfg │ │ ├── aerc.cfg │ │ ├── aerospace.cfg │ │ ├── affinity-designer.cfg │ │ ├── affinity-photo.cfg │ │ ├── affinity-publisher.cfg │ │ ├── airflow.cfg │ │ ├── airmail.cfg │ │ ├── akamai-cli.cfg │ │ ├── alacritty.cfg │ │ ├── aldente.cfg │ │ ├── alt-tab.cfg │ │ ├── amethyst.cfg │ │ ├── ancient-domains-of-mystery.cfg │ │ ├── androidstudio-13.cfg │ │ ├── ansible.cfg │ │ ├── appcleaner.cfg │ │ ├── appcode-2.cfg │ │ ├── appcode-3.cfg │ │ ├── appcode-31.cfg │ │ ├── appcode-32.cfg │ │ ├── apple-music.cfg │ │ ├── apptivate.cfg │ │ ├── arara.cfg │ │ ├── aria2c.cfg │ │ ├── arm.cfg │ │ ├── asciinema.cfg │ │ ├── asdf.cfg │ │ ├── aspell.cfg │ │ ├── astyle.cfg │ │ ├── atlantis.cfg │ │ ├── atom.cfg │ │ ├── audacious.cfg │ │ ├── auskey.cfg │ │ ├── autokey.cfg │ │ ├── awareness.cfg │ │ ├── aws.cfg │ │ ├── azure.cfg │ │ ├── b-ryan_powerline-shell.cfg │ │ ├── bartender.cfg │ │ ├── base.cfg │ │ ├── bash-it.cfg │ │ ├── bash.cfg │ │ ├── bat.cfg │ │ ├── bc.cfg │ │ ├── beatport-pro.cfg │ │ ├── beets.cfg │ │ ├── bettersnaptool.cfg │ │ ├── bettertouchtool.cfg │ │ ├── beyond-compare.cfg │ │ ├── bibdesk.cfg │ │ ├── billings-pro-server-admin.cfg │ │ ├── bitbar.cfg │ │ ├── bitchx.cfg │ │ ├── blackfire.cfg │ │ ├── blender.cfg │ │ ├── blesh.cfg │ │ ├── boto.cfg │ │ ├── boxer.cfg │ │ ├── brackets.cfg │ │ ├── brave.cfg │ │ ├── btop.cfg │ │ ├── bump.cfg │ │ ├── bundler.cfg │ │ ├── byobu.cfg │ │ ├── caffeine.cfg │ │ ├── calibre.cfg │ │ ├── capture-one.cfg │ │ ├── cartographica.cfg │ │ ├── cerebro.cfg │ │ ├── charles.cfg │ │ ├── cheat.cfg │ │ ├── chef.cfg │ │ ├── chicken.cfg │ │ ├── choosy.cfg │ │ ├── chunkwm.cfg │ │ ├── cider.cfg │ │ ├── clashx.cfg │ │ ├── clasp.cfg │ │ ├── claude-code.cfg │ │ ├── cleanshot.cfg │ │ ├── clementine.cfg │ │ ├── clion.cfg │ │ ├── clipmenu.cfg │ │ ├── clipy.cfg │ │ ├── cloudapp.cfg │ │ ├── coda-2.cfg │ │ ├── codex.cfg │ │ ├── colloquy.cfg │ │ ├── colorschemer-studio-2.cfg │ │ ├── colorslurp.cfg │ │ ├── colorsync.cfg │ │ ├── composer.cfg │ │ ├── concentrate.cfg │ │ ├── conky.cfg │ │ ├── consular.cfg │ │ ├── contexts.cfg │ │ ├── controlplane.cfg │ │ ├── copyq.cfg │ │ ├── cord.cfg │ │ ├── coteditor.cfg │ │ ├── ctags.cfg │ │ ├── curl.cfg │ │ ├── cursor.cfg │ │ ├── cvim.cfg │ │ ├── cyberduck.cfg │ │ ├── daisydisk.cfg │ │ ├── dash.cfg │ │ ├── datagrip.cfg │ │ ├── day-o.cfg │ │ ├── dbeaver.cfg │ │ ├── dbvisualizer.cfg │ │ ├── deal-alert.cfg │ │ ├── deepin-dde-dock.cfg │ │ ├── deepin-dde-file-manager.cfg │ │ ├── deepin-terminal.cfg │ │ ├── default-folder-x.cfg │ │ ├── defaultkeybinding.cfg │ │ ├── devilspie.cfg │ │ ├── devilspie2.cfg │ │ ├── dig.cfg │ │ ├── divvy.cfg │ │ ├── docker.cfg │ │ ├── dolphin.cfg │ │ ├── doom-emacs.cfg │ │ ├── doublecmd.cfg │ │ ├── doxie.cfg │ │ ├── dozer.cfg │ │ ├── draft.cfg │ │ ├── droplr.cfg │ │ ├── dropzone.cfg │ │ ├── drush.cfg │ │ ├── editorconfig.cfg │ │ ├── electrum.cfg │ │ ├── emacs.cfg │ │ ├── enjoyable.cfg │ │ ├── environmental-station-alpha.cfg │ │ ├── eqmac-2.cfg │ │ ├── eslint.cfg │ │ ├── espanso.cfg │ │ ├── exercism.cfg │ │ ├── expandrive.cfg │ │ ├── factorio.cfg │ │ ├── factory-droid.cfg │ │ ├── fantastical.cfg │ │ ├── fasd.cfg │ │ ├── fastlane.cfg │ │ ├── fastscripts.cfg │ │ ├── feeds.cfg │ │ ├── filezilla.cfg │ │ ├── finicky.cfg │ │ ├── fish.cfg │ │ ├── fisher.cfg │ │ ├── flake8.cfg │ │ ├── flameshot.cfg │ │ ├── flexget.cfg │ │ ├── flux.cfg │ │ ├── focus.cfg │ │ ├── fontconfig.cfg │ │ ├── fontexplorer-x.cfg │ │ ├── forge.cfg │ │ ├── fork.cfg │ │ ├── forklift.cfg │ │ ├── franz.cfg │ │ ├── gasmask.cfg │ │ ├── gdb.cfg │ │ ├── gearplayer.cfg │ │ ├── geektool.cfg │ │ ├── ghci.cfg │ │ ├── ghidra.cfg │ │ ├── ghostty.cfg │ │ ├── ghostwriter.cfg │ │ ├── gimp.cfg │ │ ├── git-hooks.cfg │ │ ├── git.cfg │ │ ├── gitbox.cfg │ │ ├── gitfox.cfg │ │ ├── github-cli.cfg │ │ ├── gitkraken.cfg │ │ ├── gitup.cfg │ │ ├── gmail-notifr.cfg │ │ ├── gmailctl.cfg │ │ ├── gmvault.cfg │ │ ├── gnu-stow.cfg │ │ ├── gnupg.cfg │ │ ├── go2shell.cfg │ │ ├── goku.cfg │ │ ├── goland.cfg │ │ ├── goldendict.cfg │ │ ├── goodsync.cfg │ │ ├── goshare.cfg │ │ ├── gradle.cfg │ │ ├── grandtotal-3.cfg │ │ ├── grsync.cfg │ │ ├── gstm.cfg │ │ ├── hammerspoon.cfg │ │ ├── handbrake.cfg │ │ ├── hands-off.cfg │ │ ├── hazel.cfg │ │ ├── hero-lab.cfg │ │ ├── heroku.cfg │ │ ├── hexchat.cfg │ │ ├── hexels.cfg │ │ ├── hocus-focus.cfg │ │ ├── homebrew.cfg │ │ ├── homebridge.cfg │ │ ├── houdini.cfg │ │ ├── hstr.cfg │ │ ├── htop.cfg │ │ ├── httpie.cfg │ │ ├── hub.cfg │ │ ├── hyper.cfg │ │ ├── hyperdock.cfg │ │ ├── hyperswitch.cfg │ │ ├── i2cssh.cfg │ │ ├── i3.cfg │ │ ├── idapro.cfg │ │ ├── ideavim.cfg │ │ ├── iina.cfg │ │ ├── illustrator.cfg │ │ ├── inkscape.cfg │ │ ├── insomnia.cfg │ │ ├── intellijidea.cfg │ │ ├── ipython.cfg │ │ ├── irssi.cfg │ │ ├── istat-menus.cfg │ │ ├── iterm2.cfg │ │ ├── itermocil.cfg │ │ ├── itsycal.cfg │ │ ├── itunes-scripts.cfg │ │ ├── jankyborders.cfg │ │ ├── janus.cfg │ │ ├── jitouch.cfg │ │ ├── joplin.cfg │ │ ├── jrnl.cfg │ │ ├── jsbeautifier.cfg │ │ ├── jshint.cfg │ │ ├── julia.cfg │ │ ├── jumpcut.cfg │ │ ├── jupyter.cfg │ │ ├── k9s.cfg │ │ ├── kaggle.cfg │ │ ├── kaleidoscope.cfg │ │ ├── karabiner-elements.cfg │ │ ├── karabiner.cfg │ │ ├── kdenlive.cfg │ │ ├── keepassx.cfg │ │ ├── keepassxc.cfg │ │ ├── keepingyouawake.cfg │ │ ├── keka.cfg │ │ ├── keybase.cfg │ │ ├── keyboard-maestro.cfg │ │ ├── keymo.cfg │ │ ├── keyremap4macbook.cfg │ │ ├── khd.cfg │ │ ├── kiro.cfg │ │ ├── kitty.cfg │ │ ├── krew.cfg │ │ ├── kubectl.cfg │ │ ├── kwm.cfg │ │ ├── latexit.cfg │ │ ├── launchbar.cfg │ │ ├── lazydocker.cfg │ │ ├── lazygit.cfg │ │ ├── ledger.cfg │ │ ├── leiningen.cfg │ │ ├── lf.cfg │ │ ├── libreoffice.cfg │ │ ├── liftoff.cfg │ │ ├── light-table.cfg │ │ ├── lightpaper.cfg │ │ ├── lightroom-classic.cfg │ │ ├── lightroom.cfg │ │ ├── limechat.cfg │ │ ├── liquidprompt.cfg │ │ ├── littlesnitch.cfg │ │ ├── livestreamer.cfg │ │ ├── logitech-options.cfg │ │ ├── logseq.cfg │ │ ├── lollypop.cfg │ │ ├── loopback.cfg │ │ ├── luftrausers.cfg │ │ ├── lunarvim.cfg │ │ ├── macdive2.cfg │ │ ├── macdown.cfg │ │ ├── mackup.cfg │ │ ├── macosx.cfg │ │ ├── macvim.cfg │ │ ├── magic-launch.cfg │ │ ├── magicprefs.cfg │ │ ├── magnet.cfg │ │ ├── maid.cfg │ │ ├── mail.cfg │ │ ├── mailmate.cfg │ │ ├── mailplane.cfg │ │ ├── mako.cfg │ │ ├── marked2.cfg │ │ ├── marta.cfg │ │ ├── matlab.cfg │ │ ├── maven.cfg │ │ ├── max.cfg │ │ ├── mendeley.cfg │ │ ├── menumeters.cfg │ │ ├── mercurial.cfg │ │ ├── mercurymover.cfg │ │ ├── messages.cfg │ │ ├── micro.cfg │ │ ├── microsoft-remote-desktop.cfg │ │ ├── mise.cfg │ │ ├── mitmproxy.cfg │ │ ├── mkcert.cfg │ │ ├── mole.cfg │ │ ├── monodevelop.cfg │ │ ├── moom.cfg │ │ ├── mosaic.cfg │ │ ├── mou.cfg │ │ ├── mpd.cfg │ │ ├── mplayerx.cfg │ │ ├── mps-youtube.cfg │ │ ├── mpv.cfg │ │ ├── mtmr.cfg │ │ ├── multitouch.cfg │ │ ├── mumu.cfg │ │ ├── musicbrainz-picard.cfg │ │ ├── mutespotifyads.cfg │ │ ├── mycli.cfg │ │ ├── myrepos.cfg │ │ ├── mysql.cfg │ │ ├── mysqlworkbench.cfg │ │ ├── name-mangler.cfg │ │ ├── nano.cfg │ │ ├── navicat.cfg │ │ ├── ncmpcpp.cfg │ │ ├── neofetch.cfg │ │ ├── neovim.cfg │ │ ├── nethack.cfg │ │ ├── netlify.cfg │ │ ├── newsbeuter.cfg │ │ ├── ngrok.cfg │ │ ├── ni.cfg │ │ ├── nomacs.cfg │ │ ├── nosqlbooster-for-mongodb.cfg │ │ ├── notion-enhancer.cfg │ │ ├── nova.cfg │ │ ├── npm.cfg │ │ ├── npmrc.cfg │ │ ├── nslogger.cfg │ │ ├── nuget.cfg │ │ ├── nushell.cfg │ │ ├── nvalt.cfg │ │ ├── nvm.cfg │ │ ├── nvpy.cfg │ │ ├── obs.cfg │ │ ├── oci.cfg │ │ ├── offlineimap.cfg │ │ ├── ogdesign-eagle.cfg │ │ ├── oh-my-fish.cfg │ │ ├── oh-my-tmux.cfg │ │ ├── omnifocus.cfg │ │ ├── omnigraffle.cfg │ │ ├── openbox.cfg │ │ ├── opencode.cfg │ │ ├── openemu.cfg │ │ ├── opera.cfg │ │ ├── p10k.cfg │ │ ├── paintbrush.cfg │ │ ├── pandoc.cfg │ │ ├── pass.cfg │ │ ├── pastebot.cfg │ │ ├── path-finder.cfg │ │ ├── pdfjam.cfg │ │ ├── pear.cfg │ │ ├── pentadactyl.cfg │ │ ├── perl.cfg │ │ ├── pgsql.cfg │ │ ├── phoenix.cfg │ │ ├── phoneview.cfg │ │ ├── photoshop.cfg │ │ ├── phpstorm.cfg │ │ ├── picgo.cfg │ │ ├── pidgin.cfg │ │ ├── pip.cfg │ │ ├── pixelsnap.cfg │ │ ├── pixelsnap2.cfg │ │ ├── planner.cfg │ │ ├── plover.cfg │ │ ├── pnpm.cfg │ │ ├── pock.cfg │ │ ├── podman.cfg │ │ ├── poedit.cfg │ │ ├── poetry.cfg │ │ ├── pokerstars.cfg │ │ ├── polybar.cfg │ │ ├── popclip.cfg │ │ ├── popcorn-time.cfg │ │ ├── postico.cfg │ │ ├── pow.cfg │ │ ├── powerline.cfg │ │ ├── prezto.cfg │ │ ├── processing.cfg │ │ ├── proselint.cfg │ │ ├── proxychains.cfg │ │ ├── proxyman.cfg │ │ ├── prusa-slicer.cfg │ │ ├── psysh.cfg │ │ ├── punto-switcher.cfg │ │ ├── pycharm.cfg │ │ ├── pypi.cfg │ │ ├── pyradio.cfg │ │ ├── querious.cfg │ │ ├── quicklook.cfg │ │ ├── quicksilver.cfg │ │ ├── quitter.cfg │ │ ├── qutebrowser.cfg │ │ ├── qv2ray.cfg │ │ ├── r.cfg │ │ ├── rails.cfg │ │ ├── ranger.cfg │ │ ├── rbenv.cfg │ │ ├── rclone.cfg │ │ ├── rectangle.cfg │ │ ├── redshift-scheduler.cfg │ │ ├── redshift.cfg │ │ ├── remote-desktop-manager.cfg │ │ ├── rhythmbox.cfg │ │ ├── rime.cfg │ │ ├── ripgrep.cfg │ │ ├── robo3t.cfg │ │ ├── rocket.cfg │ │ ├── rofi.cfg │ │ ├── royaltsx.cfg │ │ ├── rstudio.cfg │ │ ├── rtorrent.cfg │ │ ├── rtx.cfg │ │ ├── rubitrack5.cfg │ │ ├── rubocop.cfg │ │ ├── ruby-version.cfg │ │ ├── ruby.cfg │ │ ├── rubymine.cfg │ │ ├── rust.cfg │ │ ├── rustrover.cfg │ │ ├── rvm.cfg │ │ ├── s3cmd.cfg │ │ ├── sabnzbd.cfg │ │ ├── sbcl.cfg │ │ ├── sbt.cfg │ │ ├── scenario.cfg │ │ ├── screen.cfg │ │ ├── screenhero.cfg │ │ ├── scrivener.cfg │ │ ├── scroll-reverser.cfg │ │ ├── secure-pipes.cfg │ │ ├── securecrt.cfg │ │ ├── seil.cfg │ │ ├── selfcontrol.cfg │ │ ├── sequel-pro.cfg │ │ ├── shadowsocksx-ng.cfg │ │ ├── shiftit.cfg │ │ ├── shifty.cfg │ │ ├── shimo.cfg │ │ ├── showyedge.cfg │ │ ├── shsh-blobs.cfg │ │ ├── shuttle.cfg │ │ ├── sizeup.cfg │ │ ├── sizzy.cfg │ │ ├── sketchybar.cfg │ │ ├── skhd.cfg │ │ ├── skim.cfg │ │ ├── skitch.cfg │ │ ├── slate.cfg │ │ ├── slic3r.cfg │ │ ├── slogger.cfg │ │ ├── smartgit.cfg │ │ ├── smooth-mouse.cfg │ │ ├── soulver.cfg │ │ ├── sourcetree.cfg │ │ ├── spacelauncher.cfg │ │ ├── spacemacs.cfg │ │ ├── spacevim.cfg │ │ ├── spamsieve.cfg │ │ ├── spark.cfg │ │ ├── spectacle.cfg │ │ ├── spectrwm.cfg │ │ ├── splice.cfg │ │ ├── spotify-notifications.cfg │ │ ├── spotify.cfg │ │ ├── sqitch.cfg │ │ ├── ssh.cfg │ │ ├── starship.cfg │ │ ├── startupizer2.cfg │ │ ├── stata.cfg │ │ ├── stats.cfg │ │ ├── stay.cfg │ │ ├── storyist-3.cfg │ │ ├── streamdeck.cfg │ │ ├── subler.cfg │ │ ├── sublime-merge.cfg │ │ ├── sublime-text-2.cfg │ │ ├── sublime-text-3.cfg │ │ ├── sublime-text.cfg │ │ ├── subversion.cfg │ │ ├── superduper.cfg │ │ ├── surge.cfg │ │ ├── swaywm.cfg │ │ ├── swinsian.cfg │ │ ├── swish.cfg │ │ ├── switchhosts.cfg │ │ ├── t.cfg │ │ ├── tableplus.cfg │ │ ├── taskpaper.cfg │ │ ├── taskwarrior.cfg │ │ ├── teamocil.cfg │ │ ├── telegram_macos.cfg │ │ ├── terminal.cfg │ │ ├── terminator.cfg │ │ ├── termite.cfg │ │ ├── termux.cfg │ │ ├── terraform.cfg │ │ ├── textexpander.cfg │ │ ├── textmate.cfg │ │ ├── textual.cfg │ │ ├── things.cfg │ │ ├── tidy.cfg │ │ ├── tig.cfg │ │ ├── tiles.cfg │ │ ├── tilix.cfg │ │ ├── timeout.cfg │ │ ├── tint2.cfg │ │ ├── tinyfugue.cfg │ │ ├── tmux.cfg │ │ ├── tmuxinator.cfg │ │ ├── tmuxp.cfg │ │ ├── todotxt-cli.cfg │ │ ├── toothfairy.cfg │ │ ├── totalspaces2.cfg │ │ ├── tower-2.cfg │ │ ├── tower-3.cfg │ │ ├── tower.cfg │ │ ├── transmission.cfg │ │ ├── transmit.cfg │ │ ├── tripmode.cfg │ │ ├── trizen.cfg │ │ ├── tunnelblick.cfg │ │ ├── tvnamer.cfg │ │ ├── twitterrific.cfg │ │ ├── typinator.cfg │ │ ├── typora.cfg │ │ ├── ubersicht.cfg │ │ ├── ulauncher.cfg │ │ ├── ventrilo.cfg │ │ ├── verdaccio.cfg │ │ ├── versions.cfg │ │ ├── vim.cfg │ │ ├── vimperator.cfg │ │ ├── vimwiki.cfg │ │ ├── viscosity.cfg │ │ ├── vlc.cfg │ │ ├── volt.cfg │ │ ├── vs4mac.cfg │ │ ├── vscode-insiders.cfg │ │ ├── vscode-oss.cfg │ │ ├── vscode.cfg │ │ ├── vscodium.cfg │ │ ├── wakatime.cfg │ │ ├── warp.cfg │ │ ├── waybar.cfg │ │ ├── webstorm.cfg │ │ ├── wezterm.cfg │ │ ├── wget.cfg │ │ ├── whatsapp.cfg │ │ ├── windsurf.cfg │ │ ├── wireshark.cfg │ │ ├── witch.cfg │ │ ├── wordgrinder.cfg │ │ ├── workrave.cfg │ │ ├── wp-cli.cfg │ │ ├── x11.cfg │ │ ├── xamarinstudio-5.cfg │ │ ├── xbar.cfg │ │ ├── xbindkeys.cfg │ │ ├── xchat.cfg │ │ ├── xcode.cfg │ │ ├── xee.cfg │ │ ├── xemacs.cfg │ │ ├── xld.cfg │ │ ├── xonsh.cfg │ │ ├── xtrafinder.cfg │ │ ├── yabai.cfg │ │ ├── yarn.cfg │ │ ├── yazi.cfg │ │ ├── youtube-dl.cfg │ │ ├── yummyftp.cfg │ │ ├── zabbix-cli.cfg │ │ ├── zathura.cfg │ │ ├── zed.cfg │ │ ├── zoom.cfg │ │ ├── zoxide.cfg │ │ └── zsh.cfg │ ├── appsdb.py │ ├── config.py │ ├── constants.py │ ├── mackup.py │ ├── main.py │ └── utils.py └── tests/ ├── README.md ├── __init__.py ├── fixtures/ │ ├── .mackup/ │ │ ├── legacy-test-app.cfg │ │ └── priority-test-app.cfg │ ├── Library/ │ │ ├── Application Support/ │ │ │ └── Box/ │ │ │ └── Box Sync/ │ │ │ └── sync_root_folder.txt │ │ └── Mobile Documents/ │ │ └── com~apple~CloudDocs/ │ │ └── _blank_.md │ ├── mackup-apps_to_ignore.cfg │ ├── mackup-apps_to_ignore_and_sync.cfg │ ├── mackup-apps_to_sync.cfg │ ├── mackup-empty.cfg │ ├── mackup-engine-dropbox.cfg │ ├── mackup-engine-file_system-absolute.cfg │ ├── mackup-engine-file_system-no_path.cfg │ ├── mackup-engine-file_system.cfg │ ├── mackup-engine-google_drive.cfg │ ├── mackup-engine-icloud.cfg │ ├── mackup-engine-unknown.cfg │ ├── mackup-envarcheck.cfg │ ├── mackup-old-config.cfg │ └── xdg-config-home/ │ └── mackup/ │ ├── applications/ │ │ ├── priority-test-app.cfg │ │ └── xdg-test-app.cfg │ └── mackup.cfg ├── test_application.py ├── test_appsdb_xdg.py ├── test_backup_after_link_install.py ├── test_cli.py ├── test_config.py ├── test_config_file_option.py ├── test_constants.py ├── test_main.py └── test_utils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/CONTRIBUTING.md ================================================ # How to contribute You can: - Add or improve the support of an application (Check the [TODO][TODO] and [TOFIX][TOFIX] tasks and pick one) - Improve the Mackup codebase - You can triage issues which may include reproducing bug reports or asking for vital information, such as version numbers or reproduction instructions. If you would like to start triaging issues, one easy way to get started is to [subscribe to mackup on CodeTriage](https://www.codetriage.com/lra/mackup). [![Open Source Helpers][CODETRIAGE-IMG]][CODETRIAGE] ## Development Setup Mackup uses [uv](https://docs.astral.sh/uv/) for fast, reliable Python package management. Here's how to get started: ### Prerequisites - Python 3.9 or higher - [uv](https://docs.astral.sh/uv/) installed Install uv if you haven't already: ```bash # On macOS brew install uv # On Linux # Via pipx (recommended) pipx install uv # Or via Homebrew on Linux brew install uv # On Windows powershell -c "irm https://astral.sh/uv/install.ps1 | iex" ``` Alternatively, see the [official uv installation guide](https://docs.astral.sh/uv/getting-started/installation/) for more options. ### Setting Up Your Development Environment 1. Clone the repository: ```bash git clone https://github.com/lra/mackup.git cd mackup ``` 2. Sync dependencies (creates a virtual environment automatically): ```bash uv sync --dev ``` This will: - Create a `.venv` directory with a virtual environment - Install all project dependencies - Install development dependencies (pytest, mypy, etc.) ### Running Tests and Checks Use the provided Make targets for quick development workflow: ```bash # Run all checks (recommended before committing) make check # Run tests only make test # Run type checking make mypy # Run code linting make ruff ``` Or use `uv run` directly: ```bash # Run tests uv run pytest # Run tests with verbose output uv run pytest -v # Run type checking uv run mypy mackup/ # Run linting ruff check . ``` ### Code Quality Standards All pull requests must pass: - ✅ **Tests**: All pytest tests must pass - ✅ **Type checking**: No mypy errors - ✅ **Linting**: Code must pass ruff checks - ✅ **Formatting**: Follow existing code style Run `make check` before submitting your PR to ensure everything passes. ## Contributing Guidelines To speed up Pull Request (PR) approval and merger into Mackup, please follow these guidelines: - Keep one application supported per PR - Add the application to the list of supported applications in [README.md][README.md] - Sync configurations should follow the following principles: - Syncing should not break the application, and PRs should be tested - Syncing should not break any syncing functionality internal to the application - The configuration should sync the minimal set of data, so that syncing happens quickly. Leave large app data out of the sync configuration. - Do not sync any file or folder that represents some state, like session data, cache, any file specific to the local workstation. - Do not sync sensitive information, like clear passwords or private keys Thank you for your contribution! [TODO]: https://github.com/lra/mackup/labels/TODO [TOFIX]: https://github.com/lra/mackup/labels/TOFIX [CODETRIAGE]: https://www.codetriage.com/lra/mackup [CODETRIAGE-IMG]: https://www.codetriage.com/lra/mackup/badges/users.svg [README.md]: https://github.com/lra/mackup/blob/master/README.md ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ ### All submissions * [ ] I have followed the [Contributing Guidelines](https://github.com/lra/mackup/blob/master/.github/CONTRIBUTING.md) * [ ] I have checked to ensure there aren't other open [Pull Requests](https://github.com/lra/mackup/pulls) for the same update/change ### Adding/updating Application X Support * [ ] This PR is only for one application * [ ] It has been added to the list of supported applications in the [README](https://github.com/lra/mackup/blob/master/README.md) * [ ] The configuration syncs the minimal set of data * [ ] No file specific to the local workstation is synced * [ ] No sensitive data is synced ### Improving the Mackup codebase * [ ] My submission passes the [tests](https://github.com/lra/mackup/tree/master/tests) * [ ] I have linted the code locally prior to submission * [ ] I have written new tests as applicable * [ ] I have added an explanation of what the changes do ================================================ FILE: .github/workflows/install.yaml ================================================ name: install on: push: branches: - master pull_request: jobs: install-on-linux: strategy: matrix: os: - ubuntu-22.04 - ubuntu-22.04-arm - ubuntu-24.04 - ubuntu-24.04-arm python-version: - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" runs-on: ${{ matrix.os }} container: python:${{ matrix.python-version }} steps: - uses: actions/checkout@v6 - run: pip install . - run: mackup --help install-on-macos: strategy: matrix: os: - macos-14 - macos-15 - macos-15-intel runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 - run: pip install . - run: mackup --help ================================================ FILE: .github/workflows/linelint.yaml ================================================ # Make sure that all text files end with a newline character. # Configure your editor to end every file with a newline character. # See name: linelint on: push: branches: - master pull_request: jobs: linelint: runs-on: ubuntu-latest name: linelint steps: - name: Checkout uses: actions/checkout@v6 - name: Linelint uses: fernandrone/linelint@master ================================================ FILE: .github/workflows/markdown.yaml ================================================ name: markdown on: push: branches: - master pull_request: jobs: markdownlint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: articulate/actions-markdownlint@v1 with: config: .markdownlint.yaml ignore: 'tests/' ================================================ FILE: .github/workflows/mypy.yaml ================================================ name: mypy on: push: branches: - master pull_request: jobs: mypy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: astral-sh/setup-uv@v5 with: python-version: "3.9" - run: uv sync --all-extras --dev - run: uv run mypy src/mackup/ ================================================ FILE: .github/workflows/ruff.yaml ================================================ name: ruff on: push: branches: - master pull_request: jobs: ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: astral-sh/setup-uv@v5 - run: uv sync --all-extras --dev - run: uv run ruff check . ================================================ FILE: .github/workflows/snap.yaml ================================================ name: snap on: push: branches: - master pull_request: jobs: build-and-publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: snapcore/action-build@v1 id: build - uses: snapcore/action-publish@v1 if: github.ref == 'refs/heads/master' env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} with: snap: ${{ steps.build.outputs.snap }} release: edge ================================================ FILE: .github/workflows/test.yaml ================================================ name: test on: push: branches: - master pull_request: jobs: pytest: runs-on: ubuntu-latest strategy: matrix: python-version: - "3.9" - "3.10" - "3.11" - "3.12" - "3.13" - "3.14" steps: - uses: actions/checkout@v6 - uses: astral-sh/setup-uv@v5 with: python-version: ${{ matrix.python-version }} - run: uv sync --all-extras --dev - run: uv run pytest --cov=mackup --cov-report=term ================================================ FILE: .gitignore ================================================ # Generated when running Mackup *.pyc # Generated by make release /dist/ # Ignore Claude stuff /.claude/ # Coverage reports .coverage .coverage.* htmlcov/ coverage.xml *.cover .hypothesis/ .pytest_cache/ ================================================ FILE: .linelint.yml ================================================ # 'true' will fix files autofix: false # list of paths to ignore, uses gitignore syntaxes (executes before any rule) ignore: - tests/fixtures/Library/Application Support/Box/Box Sync/sync_root_folder.txt - tests/fixtures/Library/Mobile Documents/com~apple~CloudDocs/_blank_.md rules: # checks if file ends in a newline character end-of-file: # set to true to enable this rule enable: true # set to true to disable autofix (if enabled globally) disable-autofix: true # if true also checks if file ends in a single newline character single-new-line: true ================================================ FILE: .markdownlint.yaml ================================================ MD004: style: "dash" ================================================ FILE: .markdownlintignore ================================================ tests/ ================================================ FILE: INSTALL.md ================================================ # Detailed install instructions for Mackup There are 2 ways to run mackup: 1. Install it with Homebrew (macOS and GNU/Linux) 2. Install it with PIP (macOS and GNU/Linux) ## Install ### With Homebrew ```bash # Easy brew install mackup # Now just run it mackup -h ``` ### With Homebrew master branch for latest updates Want to install the latest master release instead of waiting on the homebrew package version? [Homebrew reference](https://docs.brew.sh/Manpage#install-options-formulacask) ```bash # Install master brew install --HEAD # Check if you are using the master or stale package brew switch mackup mackup -h ``` ### With Python's PIP ```bash # Easy too pip install mackup # Now you can run it mackup -h ``` ## Upgrade ### Upgrade with Homebrew ```bash brew update brew upgrade mackup -h ``` ### Upgrade with Python's PIP ```bash pip install --upgrade mackup mackup -h ``` ## Uninstall ### Uninstall with Homebrew ```bash brew uninstall mackup ``` ### Uninstall with Python's PIP ```bash pip uninstall mackup ``` ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 2013 Laurent Raufaste This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Mackup Copyright (C) 2013 Laurent Raufaste This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: Makefile ================================================ lint: markdownlint -c .markdownlint.yaml '**/*.md' ruff: ruff check . ty: uv run ty check test: uv run pytest coverage: uv run pytest --cov=mackup --cov-report=term --cov-report=html --cov-report=xml coverage-report: uv run coverage report mypy: uv run mypy src/mackup/ check: lint ruff mypy ty test @echo "All checks passed!" clean: rm -rf src/mackup/__pycache__ rm -rf tests/__pycache__ rm -rf dist/ rm -rf htmlcov/ rm -rf .coverage rm -rf coverage.xml release: clean uv build uv publish ================================================ FILE: README.md ================================================ # Mackup™ [![Tests](https://github.com/lra/mackup/actions/workflows/test.yaml/badge.svg)](https://github.com/lra/mackup/actions/workflows/test.yaml) [![PyPI version](https://badge.fury.io/py/mackup.svg)](https://badge.fury.io/py/mackup) [![Python Versions](https://img.shields.io/pypi/pyversions/mackup.svg)](https://pypi.org/project/mackup/) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![mypy](https://img.shields.io/badge/mypy-checked-blue)](http://mypy-lang.org/) [![License](https://img.shields.io/github/license/lra/mackup.svg)](https://github.com/lra/mackup/blob/master/LICENSE) Backup and keep your application settings in sync. ## Table of contents - [Mackup™](#mackup) - [Table of contents](#table-of-contents) - [Quickstart](#quickstart) - [Usage](#usage) - [What does it do](#what-does-it-do) - [Copy mode](#copy-mode) - [Link mode](#link-mode) - [`mackup link install`](#mackup-link-install) - [`mackup link`](#mackup-link) - [`mackup link uninstall`](#mackup-link-uninstall) - [Supported Storages](#supported-storages) - [Unsupported Storages](#unsupported-storages) - [Supported Applications](#supported-applications) - [Can you support application X](#can-you-support-application-x) - [Personalization \& configuration](#personalization--configuration) - [Why did you do this](#why-did-you-do-this) - [What platforms are supported](#what-platforms-are-supported) - [What's up with the weird name](#whats-up-with-the-weird-name) - [Architecture](#architecture) - [Where can I find more information](#where-can-i-find-more-information) ## Quickstart On macOS or Linux, if you want an easy install, you can install [Homebrew](http://brew.sh/) and do: ```bash # Install Mackup brew install mackup # Launch it and back up your files mackup backup ``` If not running macOS or Linux, or you don't like Homebrew, you can use [pip](https://pip.pypa.io/en/stable/). ```bash # Install Mackup with PIP pip install --upgrade mackup # Launch it and back up your files mackup backup ``` You're all set and can back up from now on. Next, on any new workstation, do: ```bash # Install Mackup brew install mackup # Launch it and restore your files mackup restore ``` Done! You can find more detailed instructions in [INSTALL.md](INSTALL.md). ## Usage `mackup backup` Back up your application files. Copy your local config files into the Mackup folder. `mackup restore` Restore your application settings on a newly installed workstation. Copy config files from the Mackup folder to your home folder. `mackup link install` Move your local config files into the Mackup folder, and link them to their original place. $${\color{red}warning}$$ _the `link` strategy [doesn't work correctly on macOS](#link-mode)_ `mackup link` On another workstation, links local config files from the Mackup folder. `mackup link uninstall` Copy back any synced config file to its original place. Removes the links and copies config files from the Mackup folder back into your home. `mackup list` Display the list of applications supported by Mackup. `mackup -h` Get some help, obviously... ## What does it do By only tracking pure configuration files, it keeps the crap out of your freshly new installed workstation (no cache, temporary and locally specific files are transferred). Mackup makes setting up the environment easy and simple. There are 2 modes of operations: copy mode and link mode. ### Copy mode Copy mode is used to back up and restore your files. The files are backed up into the configured Mackup folder, which can be in Dropbox, iCloud, or wherever you configure it. It is covered by the 2 commands: - `mackup backup` - `mackup restore` ### Link mode > [!WARNING] > If you are using Mackup on a current version of macOS, link mode will BREAK YOUR PREFERENCES. macOS Sonoma (macOS 14) and later don't support symlinked preferences, see [issue #2035](https://github.com/lra/mackup/issues/2035) for additional information. [PR #2085]() added copy mode, which should be used instead. Link mode is used to move your config files into the Mackup folder, and link them back to their original place. This mode is useful if you are using multiple workstations, and want to keep your application settings in sync at all times. - Backs up your application settings in a safe directory (e.g. Dropbox) - Syncs your application settings among all your workstations - Restores your configuration on any fresh install in one command line Let's take `git` as an example. Your settings for `git` are saved in your home folder, in the `.gitconfig` file. It is covered by the 3 commands: - `mackup link install` - `mackup link` - `mackup link uninstall` #### `mackup link install` If you have Dropbox, these things happen when you launch `mackup link install`: 1. `cp ~/.gitconfig ~/Dropbox/Mackup/.gitconfig` 2. `rm ~/.gitconfig` 3. `ln -s ~/Dropbox/Mackup/.gitconfig ~/.gitconfig` Now your `git` config is always backed up and up to date on all your workstations. #### `mackup link` When you launch `mackup link`, here's what it's really doing: 1. `ln -s ~/Dropbox/Mackup/.gitconfig ~/.gitconfig` That's it, you got your `git` config setup on your new workstation. `mackup` does the same for any supported application. #### `mackup link uninstall` You can revert all your files to their original state. ```bash # Just run this mackup link uninstall ``` This will remove the symlinks and copy back the files from the Mackup folder in Dropbox to their original places in your home. The Mackup folder and the files in it stay put, so that any other computer also running Mackup is unaffected. ## Supported Storages - [Dropbox](https://www.dropbox.com/) - [Google Drive](https://drive.google.com/) - [iCloud](http://www.apple.com/icloud/) - Anything able to sync a folder (e.g. [Git](http://git-scm.com/)) See the [README](doc/README.md) file in the doc directory for more info. ## Unsupported Storages - [Box](https://www.box.com): No longer supported as it ignores dotfiles, see . ## Supported Applications - [1Password 4](https://agilebits.com/onepassword) - [2Do](http://www.2doapp.com/) - [Ack](http://beyondgrep.com/) - [act](https://github.com/nektos/act) - [Adium](https://adium.im/) - [Adobe Camera Raw](http://www.adobe.com/products/photoshop/extend.html) - [Adobe Illustrator CC](https://www.adobe.com/products/illustrator.html) - [Adobe Photoshop CC](http://www.adobe.com/products/photoshop.html) - [Adobe Photoshop Lightroom CC](https://www.adobe.com/products/photoshop-lightroom.html) - [Adobe Photoshop Lightroom Classic](https://www.adobe.com/de/products/photoshop-lightroom-classic.html) - [aerc](https://aerc-mail.org/) - [AeroSpace](https://github.com/nikitabobko/AeroSpace) - [Affinity Designer](https://affinity.serif.com/designer) - [Affinity Photo](https://affinity.serif.com/photo) - [Affinity Publisher](https://affinity.serif.com/publisher) - [Airflow](https://airflowapp.com/) - [Airmail](http://airmailapp.com/) - [Akamai-CLI](https://developer.akamai.com/cli) - [Alacritty](https://github.com/jwilm/alacritty) - [AlDente](https://apphousekitchen.com/) - [AltTab](https://alt-tab-macos.netlify.app/) - [Amethyst](https://ianyh.com/amethyst/) - [Ancient Domains of Mystery](http://www.adom.de/home/index.html) - [Android Studio](https://developer.android.com/sdk/) - [Ansible](http://www.ansible.com/) - [AppCleaner](http://freemacsoft.net/appcleaner/) - [AppCode](http://www.jetbrains.com/objc/) - [Apple Music](https://www.apple.com/apple-music/) - [Apptivate](http://www.apptivateapp.com/) - [Arara](https://github.com/cereda/arara) - [aria2c](http://aria2.sourceforge.net/) - [Arm](https://www.atagar.com/arm/) - [Artistic Style](http://astyle.sourceforge.net) - [asciinema](https://asciinema.org/) - [asdf version manager](https://github.com/asdf-vm/asdf) - [Aspell](http://aspell.net/) - [Atlantis](http://www.riverdark.net/atlantis/) - [Atom](https://atom.io/) - [Audacious](http://audacious-media-player.org/) - [AusKey](https://abr.gov.au/AUSkey/) - [Autokey](https://code.google.com/p/autokey/) - [Awareness](http://iamfutureproof.com/tools/awareness/) - [AWS Command Line Interface](https://aws.amazon.com/cli/) - [ActivityWatch](http://activitywatch.net/) - [Bartender](http://www.macbartender.com/) - [Bash it](https://github.com/Bash-it/bash-it) - [Bash](http://www.gnu.org/software/bash/) - [Base](https://menial.co.uk/base/) - [Bat](https://github.com/sharkdp/bat) - [Bc](https://www.gnu.org/software/bc/) - [Beatport Pro](https://www.beatport.com/desktop) - [Beets](http://beets.io/) - [BetterSnapTool](http://www.boastr.net/) - [BetterTouchTool](http://www.boastr.net/) - [Beyond Compare](https://scootersoftware.com/) - [BibDesk](http://bibdesk.sourceforge.net/) - [Billings Pro Server Admin](https://www.marketcircle.com/billingspro/download/billingspro-server/) - [BitBar](https://getbitbar.com/) - [Bitchx](http://www.bitchx.org/) - [Blackfire](https://blackfire.io/) - [Blender](https://blender.org/) - [ble.sh](https://github.com/akinomyoga/ble.sh) - [Boto](https://github.com/boto/boto) - [Boxer](http://boxerapp.com) - [Brackets](http://brackets.io/) - [Brave](https://brave.com/) - [Btop](https://github.com/aristocratos/btop) - [Bump](https://github.com/fabiospampinato/bump) - [Bundler](http://bundler.io) - [Byobu](http://byobu.co/) - [Caffeine](http://lightheadsw.com/caffeine/) - [Calibre](https://calibre-ebook.com/) - [Capture One](http://www.phaseone.com/Imaging-Software/Capture-One.aspx) - [Cartographica](https://www.macgis.com/) - [Cerebro](https://cerebroapp.com/) - [Charles](http://www.charlesproxy.com) - [Cheat](https://github.com/chrisallenlane/cheat) - [Chef](https://www.chef.io/chef/) - [Chicken](http://sourceforge.net/projects/chicken/) - [Choosy](https://www.choosyosx.com/) - [chunkwm](https://github.com/koekeishiya/chunkwm) - [Cider](https://github.com/msanders/cider) - [ClashX](https://github.com/yichengchen/clashX) - [Clasp](https://github.com/google/clasp) - [Claude Code](https://www.claude.com/product/claude-code) - [CleanShot](https://cleanshot.com/) - [Clementine](https://www.clementine-player.org/) - [CLion](https://www.jetbrains.com/clion/) - [ClipMenu](http://www.clipmenu.com/) - [Clipy](https://clipy-app.com/) - [CloudApp](http://getcloudapp.com/) - [Coda 2](http://panic.com/coda/) - [Codex](https://openai.com/codex/) - [Colloquy](http://colloquy.info/) - [ColorSchemer Studio 2](http://www.colorschemer.com/osx_info.php) - [ColorSlurp](http://colorslurp.com/) - [ColorSync](https://en.wikipedia.org/wiki/ColorSync) - [Composer](https://getcomposer.org/) - [Concentrate](http://www.getconcentrating.com/) - [Conky](https://github.com/brndnmtthws/conky) - [Consular](https://github.com/achiu/consular) - [Contexts](https://contexts.co) - [ControlPlane](http://www.controlplaneapp.com/) - [CopyQ](https://github.com/hluk/CopyQ) - [CoRD](http://cord.sourceforge.net/) - [CotEditor](http://coteditor.com/) - [Ctags](http://ctags.sourceforge.net/) - [Cursor](https://cursor.sh/) - [cVim](https://github.com/1995eaton/chromium-vim) - [Cyberduck](https://cyberduck.io/) - [DaisyDisk](https://daisydiskapp.com) - [DataGrip](https://www.jetbrains.com/datagrip/) - [Dash](https://kapeli.com/dash) - [Day-O](http://www.shauninman.com/archive/2011/10/20/day_o_mac_menu_bar_clock) - [DBeaver](https://dbeaver.io/) - [DbVisualizer](https://www.dbvis.com/) - [Deal Alert](http://dealalertapp.com/) - [Deepin-dde-dock](https://github.com/linuxdeepin/dde-dock) - [Deepin-dde-file-manager](https://www.deepin.org/en/original/dde-file-manager/) - [Deepin-Terminal](https://github.com/linuxdeepin/deepin-terminal) - [Default Folder X](http://www.stclairsoft.com/DefaultFolderX/) - [Devil's Pie 2](http://www.gusnan.se/devilspie2/) - [Devil's Pie]() - [dig]() - [Divvy](http://mizage.com/divvy/) - [Docker](https://www.docker.com/) - [Dolphin](https://dolphin-emu.org/) - [Doom Emacs](https://github.com/hlissner/doom-emacs) - [Double Commander](http://doublecmd.sourceforge.net/) - [Doxie](http://www.getdoxie.com/) - [Dozer](https://github.com/Mortennn/Dozer) - [Draft](https://draft.sh/) - [Droplr](https://droplr.com/) - [Dropzone 3](https://aptonic.com/dropzone3/) - [Drush](http://www.drush.org/) - [Eagle (ogdesign)](https://eagle.cool/) - [EditorConfig](http://editorconfig.org/) - [Electrum](https://electrum.org/#home) - [Elgato StreamDeck](https://www.elgato.com/en/welcome-to-stream-deck) - [Emacs](http://www.gnu.org/software/emacs/) - [Enjoyable](https://yukkurigames.com/enjoyable/) - [Environmental Station Alpha](http://www.hempuli.com/esa/) - [eqMac2](https://bitgapp.com/eqmac/) - [ESLint](https://eslint.org/) - [espanso](https://espanso.org) - [Exercism](http://exercism.io/) - [ExpanDrive](http://www.expandrive.com/) - [Factorio](https://www.factorio.com) - [Factory Droid](https://factory.ai/) - [Fantastical](http://flexibits.com/fantastical) - [fasd](https://github.com/clvv/fasd) - [fastlane](https://fastlane.tools) - [FastScripts](https://redsweater.com/fastscripts/) - [Feeds](http://www.feedsapp.com/) - [FileZilla](https://filezilla-project.org/) - [Finicky](https://github.com/johnste/finicky) - [Fish](http://fishshell.com/) - [Fisher](https://github.com/jorgebucaran/fisher) - [Flake8](https://flake8.pycqa.org/) - [Flameshot](https://flameshot.org) - [FlexGet](http://flexget.com/) - [Flux](https://justgetflux.com/) - [Focus](https://heyfocus.com) - [Fontconfig](https://www.freedesktop.org/wiki/Software/fontconfig/) - [FontExplorer X](http://www.fontexplorerx.com/) - [Forge](http://www.slightlymagic.net/wiki/Forge) - [Fork](https://git-fork.com/) - [ForkLift](http://www.binarynights.com/forklift/) - [Franz](https://meetfranz.com) - [Gas Mask](https://github.com/2ndalpha/gasmask/) - [gdb](https://www.gnu.org/software/gdb/) - [Gear Player](https://www.gearmusicplayer.com/) - [GeekTool](http://projects.tynsoe.org/en/geektool/) - [GHCi](https://wiki.haskell.org/GHC/GHCi) - [Ghidra](https://ghidra-sre.org) - [Ghostty](https://ghostty.org/) - [Ghostwriter](https://wereturtle.github.io/ghostwriter/) - [Gimp](https://www.gimp.org/) - [Git Hooks](https://github.com/git-hooks/git-hooks) - [Git](http://git-scm.com/) - [Gitbox](http://gitboxapp.com/) - [GitFox](https://www.gitfox.app) - [GitHub CLI](https://cli.github.com/) - [GitKraken](https://www.gitkraken.com) - [GitUp](http://gitup.co/) - [Gmail Notifr](http://ashchan.com/projects/gmail-notifr) - [gmailctl](https://github.com/mbrt/gmailctl) - [GMVault](http://gmvault.org/) - [Gnome SSH Tunnel Manager](http://sourceforge.net/projects/gstm/) - [GnuPG](https://www.gnupg.org/) - [GNU Stow](https://www.gnu.org/software/stow/) - [Go2Shell](http://zipzapmac.com/Go2Shell) - [Goku](https://github.com/yqrashawn/GokuRakuJoudo) - [GoLand](https://www.jetbrains.com/go/) - [Goldendict](http://goldendict.org/) - [GoodSync](https://goodsync.com/) - [GoShare](https://github.com/dictget/goshare) - [Gradle](http://gradle.org) - [GrandTotal 3](http://www.mediaatelier.com/GrandTotal4/) - [grsync](http://www.opbyte.it/grsync/) - [Hammerspoon](http://www.hammerspoon.org/) - [HandBrake](https://handbrake.fr/) - [Hands Off!](http://www.oneperiodic.com/products/handsoff/) - [Hazel](http://www.noodlesoft.com/hazel.php) - [Hero Lab](http://www.wolflair.com/index.php?context=hero_lab) - [Heroku](https://www.heroku.com/) - [HexChat](https://hexchat.github.io/) - [Hexels](http://hexraystudios.com/hexels/) - [Hocus Focus](http://hocusfoc.us/) - [Homebridge](https://github.com/nfarina/homebridge) - [Homebrew](https://brew.sh) - [Houdini](http://uglyapps.co.uk/houdini/) - [Hstr](https://github.com/dvorka/hstr) - [HTML Tidy](https://www.html-tidy.org/) - [Htop](http://htop.sourceforge.net/) - [HTTPie](https://httpie.org/) - [hub](https://hub.github.com) - [Hyper.app](https://hyper.is/) - [HyperDock](https://bahoom.com/hyperdock) - [HyperSwitch](https://bahoom.com/hyperswitch) - [i2cssh](https://github.com/wouterdebie/i2cssh) - [i3](https://i3wm.org/) - [IDA Pro](https://www.hex-rays.com/products/ida/) - [IdeaVim](https://github.com/JetBrains/ideavim) - [IINA](https://iina.io) - [Inkscape](https://inkscape.org/) - [Insomnia](https://insomnia.rest/) - [IntelliJIDEA](http://www.jetbrains.com/idea/) - [IPython](http://ipython.org/) - [Irssi](http://www.irssi.org/) - [iStat Menus](https://bjango.com/mac/istatmenus/) - [Itsycal](https://github.com/sfsam/Itsycal) - [iTerm2](https://www.iterm2.com/) - [iTermocil](https://github.com/TomAnthony/itermocil) - [iTunes Scripts](https://www.apple.com/) - [JankyBorders](https://github.com/FelixKratz/JankyBorders) - [Janus](https://github.com/carlhuda/janus) - [Jitouch](http://www.jitouch.com/) - [Joplin](https://joplinapp.org/) - [jrnl](https://jrnl.sh) - [JS Beautifier](https://github.com/beautify-web/js-beautify) - [JSHint](http://jshint.com/) - [Julia](http://julialang.org) - [Jumpcut](http://jumpcut.sourceforge.net/) - [Jupyter](http://jupyter.org/) - [k9s](https://k9scli.io/) - [Kaggle](https://kaggle.com/) - [Kaleidoscope](http://www.kaleidoscopeapp.com/) - [Karabiner Elements](https://github.com/tekezo/Karabiner-Elements) - [Karabiner](https://pqrs.org/osx/karabiner/) - [Kdenlive](https://kdenlive.org/) - [KeePassX](http://www.keepassx.org/) - [KeePassXC](https://keepassxc.org/) - [KeepingYouAwake](https://github.com/newmarcel/KeepingYouAwake) - [Keka](http://www.kekaosx.com/en/) - [Keybase](https://keybase.io/) - [Keyboard Maestro](http://www.keyboardmaestro.com) - [Keymo](http://manytricks.com/keymo/) - [KeyRemap4MacBook](https://pqrs.org/osx/karabiner/) - [Khd](https://github.com/koekeishiya/khd/) - [kitty](https://sw.kovidgoyal.net/kitty/) - [Kiro](https://kiro.dev/) - [Krew](https://github.com/kubernetes-sigs/krew) - [Kubectl](https://kubernetes.io/docs/reference/kubectl/overview/) - [Kwm](https://koekeishiya.github.io/kwm/) - [LaTeXiT](http://www.chachatelier.fr/latexit/latexit-home.php?lang=en) - [LaunchBar](https://www.obdev.at/products/launchbar/index.html) - [lazydocker](https://github.com/jesseduffield/lazydocker) - [lazygit](https://github.com/jesseduffield/lazygit) - [Ledger](http://ledger-cli.org) - [Leiningen](http://leiningen.org/) - [lf](https://github.com/gokcehan/lf) - [LibreOffice](https://www.libreoffice.org/) - [Liftoff](https://github.com/thoughtbot/liftoff) - [Light Table](http://lighttable.com/) - [LightPaper](https://getlightpaper.com/) - [LimeChat](http://limechat.net/mac/) - [Liquid Prompt](https://github.com/nojhan/liquidprompt) - [LittleSnitch](http://www.obdev.at/products/littlesnitch/) - [Livestreamer](http://livestreamer.tanuki.se/) - [Logitech Options](https://www.logitech.com/en-us/product/options) - [Logseq](https://logseq.com/) - [Lollypop](https://gnumdk.github.io/lollypop-web/) - [Loopback](https://rogueamoeba.com/loopback/) - [Luftrausers](http://luftrausers.com) - [LunarVim](https://www.lunarvim.org/) - [MacDive](http://www.mac-dive.com/) - [MacDown](http://macdown.uranusjr.com/) - [MacOSX](http://www.apple.com/osx/) - [MacVim](https://github.com/macvim-dev/macvim) - [Magic Launch](https://www.oneperiodic.com/products/magiclaunch/) - [MagicPrefs](http://magicprefs.com/) - [Magnet](https://magnet.crowdcafe.com/) - [Maid](https://github.com/benjaminoakes/maid/) - [Mail](https://support.apple.com/guide/mail/welcome/mac) - [Mailmate](http://freron.com/) - [Mailplane](http://mailplaneapp.com/) - [mako](https://wayland.emersion.fr/mako/) - [Marked 2](http://marked2app.com) - [Marta](https://marta.yanex.org/) - [MATLAB](http://www.mathworks.com/products/matlab/) - [Maven](http://maven.apache.org) - [Max](http://sbooth.org/Max/) - [Mendeley Desktop](https://www.mendeley.com) - [MenuMeters](http://www.ragingmenace.com/software/menumeters/) - [Mercurial](https://www.mercurial-scm.org/) - [MercuryMover](http://www.heliumfoot.com/mercurymover/) - [Messages](http://www.apple.com/osx/apps/#messages) - [Micro](https://github.com/zyedidia/micro) - [Microsoft Azure CLI](https://github.com/Azure/azure-xplat-cli) - [Microsoft Remote Desktop](https://itunes.apple.com/us/app/microsoft-remote-desktop-10/id1295203466) - [mise-en-place](https://github.com/jdx/mise) - [mitmproxy](https://mitmproxy.org/) - [mkcert](https://github.com/FiloSottile/mkcert) - [Mole](https://github.com/tw93/Mole) - [MonoDevelop](http://www.monodevelop.com) - [Moom](http://manytricks.com/moom/) - [Mosaic](https://lightpillar.com/mosaic.html) - [Mou](http://25.io/mou/) - [mpd](http://www.musicpd.org) - [MPlayerX](http://mplayerx.org) - [MPS Youtube](https://github.com/mps-youtube/mps-youtube) - [MPV](https://mpv.io/) - [MTMR](https://github.com/Toxblh/MTMR) - [Multitouch](https://multitouch.app/) - [Mumu](https://getmumu.com) - [MusicBrainz Picard](https://picard.musicbrainz.org/) - [MuteSpotifyAds](https://github.com/simonmeusel/MuteSpotifyAds) - [mycli](https://www.mycli.net/) - [myrepos](https://github.com/joeyh/myrepos) - [MySQL Workbench](https://www.mysql.com/products/workbench/) - [MySQL](http://www.mysql.com/) - [Name Mangler](http://manytricks.com/namemangler/) - [Nano](http://www.nano-editor.org/) - [Navicat](http://navicat.com/) - [ncmpcpp](http://rybczak.net/ncmpcpp/) - [Neofetch](https://github.com/dylanaraps/neofetch) - [neovim](https://github.com/neovim/neovim) - [Nethack](http://www.nethack.org) - [Netlify](https://www.netlify.com/) - [newsbeuter](http://newsbeuter.org/) - [ngrok](https://ngrok.com/) - [ni](https://github.com/antfu/ni/) - [Nomacs](http://nomacs.org/) - [NoSQLBooster for MongoDB](https://www.nosqlbooster.com/) - [notion-enhancer](https://notion-enhancer.github.io/) - [Nova](https://www.nova.app/) - [npm](https://www.npmjs.com/) - [npmrc](https://github.com/deoxxa/npmrc/) - [NSLogger](https://github.com/fpillet/NSLogger) - [nuget](https://www.nuget.org/) - [Nushell](https://www.nushell.sh/) - [nvALT](http://brettterpstra.com/projects/nvalt/) - [nvm](https://github.com/nvm-sh/nvm) - [nvpy](https://github.com/cpbotha/nvpy) - [OBS](https://obsproject.com) - [OfflineIMAP](https://www.offlineimap.org/) - [Oh My Fish](https://github.com/bpinto/oh-my-fish) - [Oh My Tmux](https://github.com/gpakosz/.tmux) - [OmniFocus](https://www.omnigroup.com/omnifocus/) - [OmniGraffle](https://www.omnigroup.com/omnigraffle/) - [Openbox](http://openbox.org) - [OpenCode](https://opencode.ai) - [OpenEmu](http://openemu.org) - [OpenSSH](http://www.openssh.com/) - [Opera](http://www.opera.com) - [Oracle Cloud Infrastructure CLI](https://docs.oracle.com/en-us/iaas/Content/API/Concepts/cliconcepts.htm) - [Paintbrush](http://paintbrush.sourceforge.net/) - [Pandoc](http://pandoc.org) - [Pass](http://www.passwordstore.org/) - [Pastebot](http://tapbots.com/software/pastebot/) - [Path Finder](http://www.cocoatech.com/pathfinder/) - [PDFjam](https://warwick.ac.uk/fac/sci/statistics/staff/academic-research/firth/software/pdfjam/) - [Pear](http://pear.php.net/) - [Pentadactyl](http://5digits.org/pentadactyl/) - [Perl](https://www.perl.org/) - [Phoenix](https://github.com/kasper/phoenix) - [PhoneView](https://www.ecamm.com/mac/phoneview/) - [PhpStorm](http://www.jetbrains.com/phpstorm/) - [PicGo](https://github.com/Molunerfinn/PicGo) - [Pidgin](https://www.pidgin.im) - [PIP](http://www.pip-installer.org/) - [PixelSnap](https://getpixelsnap.com/) - [PixelSnap 2](https://getpixelsnap.com/) - [Planner](https://useplanner.com/) - [Plover](http://www.openstenoproject.org/plover/) - [Pnpm](https://pnpm.js.org/) - [Pock](https://pock.pigigaldi.com) - [Podman](https://podman.io/) - [Poedit](http://poedit.net/) - [Poetry](https://python-poetry.org) - [PokerStars](https://www.pokerstars.com/) - [Polybar](https://polybar.github.io/) - [PopClip](http://pilotmoon.com/popclip/) - [Popcorn-Time](https://popcorntime.io/) - [PostgreSQL](http://www.postgresql.org/) - [Postico](https://eggerapps.at/postico/) - [Pow](http://pow.cx/) - [Powerlevel10k](https://github.com/romkatv/powerlevel10k) - [Powerline](https://github.com/powerline/powerline) - [Powerline-shell](https://github.com/b-ryan/powerline-shell) - [Prezto](https://github.com/sorin-ionescu/prezto) - [Processing](https://processing.org/) - [Proselint](https://github.com/amperser/proselint) - [ProxyChains NG](http://sourceforge.net/projects/proxychains-ng/) - [ProxyChains](http://proxychains.sourceforge.net) - [Proxyman](https://proxyman.io) - [PrusaSlicer](https://www.prusa3d.com/prusaslicer/) - [PsySH](https://psysh.org/) - [Punto Switcher](https://punto.yandex.ru/) - [PyCharm](https://www.jetbrains.com/pycharm/) - [PyPI](https://pypi.python.org/pypi) - [PyRadio](http://www.coderholic.com/pyradio/) - [Querious](http://www.araelium.com/querious/) - [Quicksilver](http://qsapp.com/) - [Quitter](https://marco.org/apps) - [Qutebrowser](http://qutebrowser.org/) - [Qv2ray](https://qv2ray.net/) - [R](http://www.r-project.org/) - [Rails](http://rubyonrails.org/) - [Ranger](https://ranger.github.io/) - [Rbenv](https://www.github.com/rbenv/rbenv) - [Rclone](https://rclone.org/) - [Rectangle](https://rectangleapp.com/) - [Redshift Scheduler](https://github.com/spantaleev/redshift-scheduler) - [Redshift](http://jonls.dk/redshift/) - [Remote Desktop Manager](https://remotedesktopmanager.com/) - [Rhythmbox](https://wiki.gnome.org/Apps/Rhythmbox) - [Rime](http://rime.im/) - [ripgrep](https://github.com/BurntSushi/ripgrep) - [Robo 3T](http://robomongo.org/) - [Rocket](https://matthewpalmer.net/rocket/) - [Rofi](https://github.com/DaveDavenport/rofi) - [Royal TSX](http://www.royaltsx.com/ts/osx/features) - [RStudio](https://www.rstudio.com/) - [rTorrent](http://libtorrent.rakshasa.no/) - [rtx](https://github.com/jdx/rtx) - [rubiTrack 5](https://www.rubitrack.com) - [Rubocop](https://github.com/bbatsov/rubocop) - [Ruby Version Manager](https://rvm.io/) - [Ruby Version](https://gist.github.com/fnichol/1912050) - [Ruby](https://www.ruby-lang.org/) - [RubyMine](http://www.jetbrains.com/ruby/) - [Rust](https://www.rust-lang.org/) - [RustRover](https://www.jetbrains.com/rust/) - [S3cmd](http://s3tools.org/s3cmd) - [SABnzbd](http://sabnzbd.org/) - [SBCL](http://www.sbcl.org/) - [SBT](http://www.scala-sbt.org/) - [Scenario](http://www.lagentesoft.com/scenario/) - [Screen](http://www.gnu.org/software/screen/) - [Screenhero](https://screenhero.com) - [Scrivener](http://www.literatureandlatte.com/scrivener.php) - [Scroll Reverser](https://pilotmoon.com/scrollreverser/) - [SecureCRT](https://www.vandyke.com/products/securecrt/) - [Secure Pipes](http://www.opoet.com/) - [Seil](https://pqrs.org/osx/karabiner/seil.html.en) - [SelfControl](http://selfcontrolapp.com/) - [Sequel Pro](http://www.sequelpro.com/) - [ShadowsocksX-NG](https://github.com/shadowsocks/ShadowsocksX-NG) - [ShiftIt](https://github.com/fikovnik/ShiftIt) - [Shifty](https://shifty.natethompson.io/) - [Shimo](https://www.feingeist.io/shimo/) - [ShowyEdge](https://pqrs.org/osx/ShowyEdge/index.html.en) - [SHSH Blobs](https://en.wikipedia.org/wiki/SHSH_blob) - [Shuttle](http://fitztrev.github.io/shuttle/) - [SizeUp](http://www.irradiatedsoftware.com/sizeup/) - [Sizzy](https://sizzy.co/) - [SketchyBar](https://felixkratz.github.io/SketchyBar/) - [skhd](https://github.com/koekeishiya/skhd/) - [Skim](http://skim-app.sourceforge.net/) - [Skitch](https://evernote.com/skitch/) - [Slate](https://github.com/jigish/slate) - [Slic3r](http://slic3r.org) - [Slogger](http://brettterpstra.com/projects/slogger/) - [SmartGit](http://www.syntevo.com/smartgit/) - [Smooth Mouse](http://smoothmouse.com/) - [Soulver](http://www.acqualia.com/soulver/) - [SourceTree](https://www.sourcetreeapp.com/) - [SpaceLauncher](https://spacelauncherapp.com) - [Spacemacs](https://github.com/syl20bnr/spacemacs) - [SpaceVim](https://github.com/SpaceVim/SpaceVim) - [SpamSieve](https://c-command.com/spamsieve) - [Spark](http://www.shadowlab.org/softwares/spark.php) - [Spectacle](https://www.spectacleapp.com/) - [Spectrwm](https://github.com/conformal/spectrwm/wiki) - [Splice](https://splice.com/) - [Spotify Notifications](http://spotify-notifications.citruspi.io/) - [Spotify](https://www.spotify.com/) - [Sqitch](https://sqitch.org/) - [Starship](https://starship.rs/) - [Startupizer2](http://appledoc.gentlebytes.com/startupizer/) - [Stata](http://www.stata.com/) - [Stats](https://github.com/exelban/stats) - [Stay](https://cordlessdog.com/stay/) - [Storyist](http://storyist.com/) - [Subler](https://subler.org) - [Sublime Merge](https://www.sublimemerge.com/) - [Sublime Text](http://www.sublimetext.com/) - [Subversion](http://subversion.apache.org/) - [SuperDuper!](http://www.shirt-pocket.com/SuperDuper/SuperDuperDescription.html) - [Surge](http://surge.run/manual/) - [Sway](https://swaywm.org/) - [Swinsian](http://swinsian.com/) - [Swish](https://highlyopinionated.co/swish/) - [SwitchHosts](https://github.com/oldj/SwitchHosts) - [T](http://sferik.github.io/t/) - [TablePlus](https://tableplus.io) - [TaskPaper](https://www.taskpaper.com) - [Taskwarrior](http://taskwarrior.org/) - [Teamocil](https://github.com/remi/teamocil) - [Telegram for macOS](https://macos.telegram.org) - [Terminal](http://www.apple.com/osx/apps/) - [Terminator](https://launchpad.net/terminator/) - [termite](https://github.com/thestinger/termite) - [Termux](https://termux.dev/) - [Terraform](https://developer.hashicorp.com/terraform) - [TextExpander](https://smilesoftware.com/textexpander) - [TextMate](http://macromates.com/) - [Textual](http://www.codeux.com/textual/) - [Things](https://culturedcode.com/things/) - [Tig](https://github.com/jonas/tig) - [Tiles](https://www.sempliva.com/tiles/) - [Tilix](https://github.com/gnunn1/tilix) - [Timeout](https://www.dejal.com/timeout/) - [tint2](https://code.google.com/p/tint2/) - [TinyFugue](http://tinyfugue.sourceforge.net) - [Tmux](http://tmux.sourceforge.net/) - [Tmuxp](https://github.com/tony/tmuxp) - [Tmuxinator](https://github.com/tmuxinator/tmuxinator) - [Todo.txt CLI](http://todotxt.com/) - [ToothFairy](https://c-command.com/toothfairy/) - [TotalSpaces2](http://totalspaces.binaryage.com/) - [Tower](http://www.git-tower.com/) - [Transmission](http://www.transmissionbt.com/) - [Transmit](http://panic.com/transmit/) - [TripMode](https://www.tripmode.ch) - [Trizen](https://github.com/trizen/trizen) - [Tunnelblick](https://tunnelblick.net) - [tvnamer](https://github.com/dbr/tvnamer) - [Twitterrific](http://twitterrific.com/) - [Typinator](http://www.ergonis.com/products/typinator/) - [Typora](https://typora.io) - [uTorrent](http://www.utorrent.com/) - [ulauncher](https://ulauncher.io/) - [Ventrilo](http://www.ventrilo.com/) - [Verdaccio](https://verdaccio.org/) - [Versions](http://www.versionsapp.com) - [Vim](http://www.vim.org/) - [Vimperator](http://www.vimperator.org/vimperator) - [Vimwiki](https://vimwiki.github.io/) - [Viscosity](http://www.sparklabs.com/viscosity/) - [Visual Studio Code](https://code.visualstudio.com/) - [Visual Studio Code - Insiders](https://code.visualstudio.com/insiders) - [Visual Studio Code - OSS](https://github.com/Microsoft/vscode) - [VSCodium](https://vscodium.com/) - [Visual Studio for Mac](https://www.visualstudio.com/vs/visual-studio-mac/) - [VLC](http://www.videolan.org/) - [Volt](https://github.com/vim-volt/volt) - [Wakatime](https://wakatime.com/) - [Warp](https://www.warp.dev) - [waybar](https://github.com/Alexays/Waybar) - [WebStorm](https://www.jetbrains.com/webstorm/) - [WezTerm](https://wezfurlong.org/wezterm/) - [Wget](https://www.gnu.org/software/wget/) - [WhatsApp Web](https://web.whatsapp.com/) - [Windsurf](https://www.codeium.com) - [Wireshark 2](https://www.wireshark.org) - [Witch](http://manytricks.com/witch/) - [WordGrinder](https://cowlark.com/wordgrinder/) - [WordPress WP-CLI](http://wp-cli.org/) - [Workrave](http://www.workrave.org/) - [X11](http://www.x.org/) - [Xee](https://theunarchiver.com/xee) - [Xamarin Studio](https://xamarin.com/studio) - [xbar](https://xbarapp.com/) - [XBindKeys](http://www.nongnu.org/xbindkeys/) - [Xchat](http://xchat.org/) - [Xcode](https://developer.apple.com/xcode/) - [XEmacs](http://www.xemacs.org/) - [XLD](http://tmkk.undo.jp/xld/) - [Xonsh](https://xon.sh) - [XtraFinder](http://www.trankynam.com/xtrafinder/) - [yabai](https://github.com/koekeishiya/yabai) - [yarn](https://yarnpkg.com) - [yazi](https://github.com/sxyazi/yazi) - [youtube-dl](https://ytdl-org.github.io/youtube-dl/) - [Yummy FTP](http://www.yummysoftware.com/) - [zabbix-cli](https://github.com/usit-gd/zabbix-cli) - [zathura](https://pwmt.org/projects/zathura/) - [zed](https://zed.dev/) - [Zoom](http://zoom.com/) - [zoxide](https://github.com/ajeetdsouza/zoxide) - [Zsh](http://zsh.sourceforge.net/) - [Übersicht](http://tracesof.net/uebersicht/) ## Can you support application X We can [with your help](doc#get-official-support-for-an-application) ;) ## Personalization & configuration Have an application that shouldn't be generally supported but that you use? Or some personal files you want to sync, e.g. various config files in a `~/.config/` directory or your personal `~/.gitignore`? - Create a `~/.mackup` directory to [sync an application or any file or directory](doc#add-support-for-an-application-or-any-file-or-directory) ## Why did you do this Yesterday, I had a talk with [Zach Zaro](http://zacharyzaro.com/), complaining about the pain it is to reconfigure our Macbook each time we get a new one or install from scratch. That's a talk we have already had months ago. I change my workstation every X months. Each time I either lose my apps' configurations, or I just waste a bunch of hours getting setup like I was on my old box. I also spend a lot of time reconfiguring the same stuff again on all my workstations (home, work). Boring... Some people tried to solve the problem on the application layer, like [Github's Boxen](https://boxen.github.com/), but it solves a different problem, from my point of view. I don't spend a lot of time installing or downloading stuff. I spend time configuring it. For years, I've used a personal shell script that was copying known config files into Subversion, Git or Dropbox, and linked them into my home. But I felt a lot of us had the same problem: Making a more generic tool could help others and I could get help from others to support more apps in the tool. So here comes Mackup, the little tool that will sync all your application configs to Dropbox (or Google Drive, or anything). And it's [GPL](http://www.gnu.org/licenses/gpl.html), of course. ## What platforms are supported - macOS - GNU/Linux ## What's up with the weird name Mackup is just a portmanteau of Mac and Backup. It is simple, short, and easy to remember, and it corresponds with the whole idea of Mackup: the simpler – the better! (And I suck at naming stuff, but who doesn't.) ## Architecture Want to understand how Mackup works internally? Check out the [Architecture Guide](doc/ARCHITECTURE.md) which includes: - Visual architecture diagram - Component breakdown - Data flow diagrams - Design decisions - Extension points for contributors Perfect for contributors who want to understand the codebase or users curious about how their configs are managed. ## Where can I find more information In the [doc](doc) directory. ================================================ FILE: doc/.mackup/gnupg.cfg ================================================ [application] name = GnuPG [configuration_files] .gnupg/gpg-agent.conf .gnupg/gpg.conf .gnupg/secring.gpg .gnupg/trustdb.gpg .gnupg/pubring.gpg .gnupg/pubring.kbx ================================================ FILE: doc/.mackup/mackup.cfg ================================================ [application] name = Mackup [configuration_files] .mackup.cfg .mackup ================================================ FILE: doc/.mackup/my-own-files.cfg ================================================ [application] name = Files and directories I want to sync [configuration_files] bin src/mackup ================================================ FILE: doc/.mackup/ssh.cfg ================================================ [application] name = SSH [configuration_files] .ssh ================================================ FILE: doc/.mackup.cfg ================================================ # # Sample Mackup configuration file # # You can specify the storage type Mackup will use to store your configuration # files. # For now you have 3 options: "dropbox", "google_drive" and "file_system". # If none is specified, Mackup will try to use the default: "dropbox". # With the "dropbox" storage engine, Mackup will automatically figure out your # Dropbox folder. [storage] engine = dropbox # If you choose the "google_drive" storage engine instead, Mackup will figure # out where your Google Drive is and store your configuration files in it. # [storage] # engine = google_drive # If you want to specify another directory, you can use the "file_system" # engine and Mackup won't try to detect any path for you: it will store your # files where you explicitly told him to, using the "path" setting. # The "path" can be absolute (from the / of your drive) or relative to your # home directory. # The "path" setting is mandatory when using the "file_system" engine. # Note: you don't need to escape spaces or wrap the path in quotes. # [storage] # engine = file_system # path = some/folder/in/your/home # # or # path = /some/folder/in/your/root # # or # path = /some path/in/your/root # You can customize the directory name in which Mackup stores your file. By # default, if not specified, Mackup creates a "Mackup" directory in the storage # engine you chose, e.g. "~/Dropbox/Mackup". directory = Mackup # List of applications you want to explicitly sync # One application name per line # If this list is empty, Mackup will try to sync all the supported # applications. # # To see a list of supported application names, launch mackup list # Use the names listed below. # # For example, to sync Irssi, Ventrilo and XEmacs, add: [applications_to_sync] irssi ventrilo xemacs # List of applications you want to ignore # One application name per line # If an application is ignored, it will be ignored even if it's been explicitly # allowed in the [Allowed Applications]. # # To see a list of supported application names, launch mackup list # Use the names listed below. # # For example, to not sync SSH and Adium, add: [applications_to_ignore] ssh adium ================================================ FILE: doc/ARCHITECTURE.md ================================================ # Mackup Architecture This document explains how Mackup works internally to help contributors understand the codebase. ## Visual Overview ![Mackup Architecture Diagram](architecture.svg) ## Core Concepts Mackup is a tool that backs up and syncs application configuration files across multiple machines using a cloud storage service (Dropbox, Google Drive, iCloud) or any file system location. ### Operation Modes Mackup has two main operation modes: #### 1. Copy Mode (Recommended) Copy mode is used for backing up and restoring files: - **`mackup backup`**: Copies configuration files from your home directory to the Mackup folder - **`mackup restore`**: Copies configuration files from the Mackup folder back to your home directory This is a one-time operation used when setting up a new machine or creating an initial backup. #### 2. Link Mode (Legacy - Not recommended for macOS Sonoma+) Link mode creates persistent symlinks between your home directory and the Mackup folder: - **`mackup link install`**: Moves files to Mackup folder and creates symlinks back to original locations - **`mackup link`**: Creates symlinks from Mackup folder to home directory (on additional machines) - **`mackup link uninstall`**: Removes symlinks and copies files back to original locations ⚠️ **Warning**: Link mode is broken on macOS Sonoma (14) and later due to changes in how macOS handles symlinked preferences. Use copy mode instead. ## Component Architecture ### 1. Command Line Interface (`main.py`) Entry point for the application. Parses command-line arguments using `docopt` and dispatches to the appropriate operation. **Key Functions:** - Argument parsing and validation - Help text display - Operation dispatch ### 2. Configuration Manager (`config.py`) Reads and parses the `.mackup.cfg` configuration file. **Configuration Sources (in order of precedence):** 1. `~/.mackup.cfg` 2. `$MACKUP_CONFIG` environment variable 3. `$XDG_CONFIG_HOME/mackup/mackup.cfg` or `~/.config/mackup/mackup.cfg` **Configuration Options:** - Storage engine (dropbox, google_drive, icloud, file_system) - Storage path and directory name - Applications to sync/ignore - Custom directory name ### 3. Application Database (`appsdb.py`) Manages the database of supported applications and their configuration files. **Sources:** - Built-in application configs (`mackup/applications/*.cfg`) - User-defined custom configs (`~/.mackup/*.cfg`) **Application Config Format:** ```ini [application] name = Application Name [configuration_files] .config_file folder/ [xdg_configuration_files] app/config ``` ### 4. Application Handler (`application.py`) Handles the operations (backup, restore, link) for individual applications. **Key Operations:** - File discovery and validation - Copy operations with permission preservation - Symlink creation and management - XDG directory handling - Conflict detection ### 5. Core Engine (`mackup.py`) Orchestrates the overall backup/restore/link operations across all applications. **Workflow:** 1. Load configuration 2. Determine storage location 3. Load application database 4. Filter applications based on config 5. Execute operation on each application 6. Handle errors and report status ### 6. Utilities (`utils.py`) Common utility functions used throughout the codebase: - File system operations (copy, delete, symlink) - Path manipulation and resolution - XDG directory detection - Error handling and user prompts - Storage engine detection (Dropbox, Google Drive, iCloud) ## Data Flow ### Backup Flow ```text User runs: mackup backup ↓ main.py parses command ↓ config.py loads .mackup.cfg ↓ appsdb.py loads application definitions ↓ mackup.py iterates through applications ↓ application.py for each app: - Finds config files in home directory - Copies to Mackup storage folder - Preserves permissions and timestamps ↓ Files now in: ~/Dropbox/Mackup/ (or chosen storage) ``` ### Restore Flow ```text User runs: mackup restore ↓ main.py parses command ↓ config.py loads .mackup.cfg ↓ appsdb.py loads application definitions ↓ mackup.py iterates through applications ↓ application.py for each app: - Finds files in Mackup storage folder - Copies to home directory - Preserves permissions and timestamps ↓ Config files now in home directory ``` ### Link Install Flow (Legacy) ```text User runs: mackup link install ↓ main.py parses command ↓ config.py loads .mackup.cfg ↓ appsdb.py loads application definitions ↓ mackup.py iterates through applications ↓ application.py for each app: - Moves config files to Mackup storage folder - Creates symlinks from original location to storage ↓ Files in storage: ~/Dropbox/Mackup/ Symlinks in home: ~/.config → ~/Dropbox/Mackup/.config ``` ## Key Design Decisions ### 1. Symlinks vs Copies **Original Design (Link Mode):** - Symlinks allowed real-time sync across machines - Changes immediately reflected in cloud storage - Single source of truth **Current Recommendation (Copy Mode):** - macOS Sonoma+ broke symlink support for preferences - Copy mode is more reliable across platforms - Trade-off: manual sync required (re-run backup/restore) ### 2. Application Database - **Centralized definitions**: All supported apps in `mackup/applications/` - **User extensibility**: Custom apps via `~/.mackup/*.cfg` - **Simple format**: INI-style configuration files - **Override capability**: User configs override built-in ones ### 3. Storage Abstraction - **Multiple backends**: Dropbox, Google Drive, iCloud, file system - **Auto-detection**: Automatically finds storage paths - **Flexibility**: Custom paths for any sync solution ### 4. XDG Support - **Standards compliance**: Respects XDG Base Directory Specification - **Flexibility**: Handles non-standard `$XDG_CONFIG_HOME` - **Separate section**: `[xdg_configuration_files]` in app configs ## File Structure ```text mackup/ ├── main.py # CLI entry point ├── mackup.py # Core orchestration engine ├── config.py # Configuration management ├── appsdb.py # Application database ├── application.py # Per-application operations ├── utils.py # Utility functions ├── constants.py # Constants and defaults └── applications/ # Built-in app configs ├── git.cfg ├── vim.cfg ├── ssh.cfg └── ... (600+ more) ``` ## Extension Points ### Adding a New Application Create `mackup/applications/myapp.cfg`: ```ini [application] name = My Application [configuration_files] .myapprc .myapp/ [xdg_configuration_files] myapp/config.yml ``` Submit PR with the new config file. ### Adding a New Storage Backend 1. Extend `config.py` to detect new storage location 2. Add storage type to `constants.py` 3. Implement path detection in `utils.py` 4. Update documentation ### Custom File Sync Users can sync any files by creating `~/.mackup/custom.cfg`: ```ini [application] name = My Custom Files [configuration_files] .custom_file custom_directory/ ``` ## Testing Strategy The codebase includes comprehensive unit tests: - **`tests/test_application.py`**: Application operations - **`tests/test_config.py`**: Configuration parsing - **`tests/test_utils.py`**: Utility functions - **`tests/test_cli.py`**: Command-line interface - **`tests/test_main.py`**: Main entry point Tests use temporary directories and mock file systems to avoid affecting the actual system. ## Error Handling Mackup includes several safety mechanisms: 1. **Confirmation prompts**: Asks before destructive operations 2. **Conflict detection**: Warns if files exist in both locations 3. **Dry-run capability**: Can preview operations (future enhancement) 4. **Graceful degradation**: Continues with other apps if one fails 5. **Clear error messages**: Helps users understand what went wrong ## Performance Considerations - **Sequential processing**: Processes one application at a time - **File-by-file operations**: No batch operations for reliability - **No caching**: Reads fresh data on each run - **Minimal dependencies**: Only requires `docopt-ng` for CLI parsing ## Future Enhancements Potential improvements to the architecture: 1. **Progress indicators**: Show progress during long operations 2. **Dry-run mode**: Preview changes without executing 3. **Incremental sync**: Only sync changed files 4. **Parallel processing**: Speed up operations on multi-app backups 5. **Conflict resolution**: Better handling of file conflicts 6. **Rollback capability**: Undo operations if something goes wrong ## Contributing See [CONTRIBUTING.md](../.github/CONTRIBUTING.md) and [develop.md](develop.md) for guidelines on contributing to Mackup. ================================================ FILE: doc/README.md ================================================ # Configuration > 💡 **New to Mackup?** Check out the > [Architecture Guide](ARCHITECTURE.md) to understand how Mackup works > under the hood. All the configuration is done in a file named `.mackup.cfg` stored at the root of your home folder. This location can be overridden via environment variables or the `--config-file` command-line option. To configure Mackup, create a file named `.mackup.cfg` in your home directory. ```bash vi ~/.mackup.cfg ``` ## Configuration file location Config files are searched in the following order. If none is found, Mackup will use the default config location of `~/.mackup.cfg` - `~/.mackup.cfg` - `$MACKUP_CONFIG` - `$XDG_CONFIG_HOME/mackup/mackup.cfg` or `~/.config/mackup/mackup.cfg` You can also specify a custom config file location using the `--config-file` command-line option: ```bash mackup --config-file ~/.mackup-custom.cfg backup ``` The path can be absolute or relative to your home directory. Note that the config file must be located within your home directory for security reasons. ## Storage You can specify the storage type Mackup will use to store your configuration files. For now, you have 4 options: `dropbox`, `google_drive`, `icloud` and `file_system`. If none is specified, Mackup will try to use the default: `dropbox`. With the `dropbox` storage engine, Mackup will automatically figure out your Dropbox folder. ### Dropbox ```ini [storage] engine = dropbox ``` ### Google Drive If you choose the `google_drive` storage engine instead, Mackup will figure out where your Google Drive is and store your configuration files in it. ```ini [storage] engine = google_drive ``` ### iCloud If you choose the `iCloud` storage engine, Mackup will store your configuration files in the `~/Library/Mobile\ Documents/com\~apple\~CloudDocs/` folder. ```ini [storage] engine = icloud ``` You can check if your files are synced using: ```sh brctl monitor com.apple.CloudDocs ``` ### File System If you want to specify another directory, you can use the `file_system` engine and Mackup won't try to detect any path for you: it will store your files where you explicitly told it to, using the `path` setting. The `path` can be absolute (from the `/` of your drive) or relative to your home directory. The `path` setting is mandatory when using the `file_system` engine. ```ini [storage] engine = file_system path = some/folder/in/your/home # or path = /some/folder/in/your/root ``` Note: you don't need to escape spaces or wrap the path in quotes. For example, the following paths are valid: ```ini path = some/path in your/home path = /some path/in/your/root ``` ### Custom Directory Name You can customize the directory name in which Mackup stores your files. By default, if not specified, Mackup creates a `Mackup` directory in the storage engine you chose, e.g. `~/Dropbox/Mackup`. ```ini [storage] directory = Mackup ``` For example: ```ini [storage] engine = file_system path = dotfiles directory = backup ``` This will store your files in the `~/dotfiles/backup` directory in your home. You can also select a subfolder: ```ini [storage] engine = icloud directory = .config/mackup ``` ### Switching Storage If you ever change your mind and switch storage solutions after Mackup is already setup (ex: from `dropbox` to `icloud`), complete the following steps. 1. Run `mackup uninstall` on all computers 2. Copy your Mackup files to the new storage location 3. Change the storage provider details in your `.mackup.cfg` file (see above) 4. Run `mackup backup` on the main computer and `mackup restore` on all others ## Applications ### Only sync one or two applications In your home folder, create a file named `.mackup.cfg` and add the application names to allow in the `[applications_to_sync]` section, one per line. ```ini # Example, to only sync SSH and Adium: [applications_to_sync] ssh adium ``` Use `mackup list` to get a list of valid application names. Don't use fancy names (with spaces) here. A [sample](.mackup.cfg) of this file is available in this folder. Just copy it to your home folder: ```bash cp mackup/doc/.mackup.cfg ~/ ``` ### Don't sync an application In your home folder, create a file named `.mackup.cfg` and add the application names to ignore in the `[applications_to_ignore]` section, one per line. ```ini # Example, to not sync SSH and Adium: [applications_to_ignore] ssh adium ``` Use `mackup list` to get a list of valid application names. Don't use fancy names (with spaces) here. A [sample](.mackup.cfg) of this file is available in this folder. Just copy it to your home folder: ```bash cp mackup/doc/.mackup.cfg ~/ ``` ### Get official support for an application Open a [new issue](https://github.com/lra/mackup/issues) and ask for it, or fork Mackup and open a [Pull Request](https://help.github.com/articles/using-pull-requests). The stock application configs are in the `mackup/applications` directory. Remember to follow the guidelines in [CONTRIBUTING.md](https://github.com/lra/mackup/blob/master/.github/CONTRIBUTING.md) to get your Pull Request merged faster. ### Add support for an application or (almost) any file or directory You can customize the Mackup engine and add support for unsupported applications or just custom files and directories you'd like to sync. NOTE: Files and directories to be synced should be rooted at $HOME. Let's say that you'd like to add support for Nethack (config file: `.nethackrc`), for the `bin` and `.hidden` directories and for the `.gitignore` file you keep in your home. In your home, create a `.mackup` directory and add a config file for the application you'd like to support: ```bash mkdir ~/.mackup touch ~/.mackup/nethack.cfg touch ~/.mackup/my-files.cfg ``` #### Custom applications directory location Custom application configs are searched in the following order: 1. `~/.mackup/` (legacy location, takes priority) 2. `$XDG_CONFIG_HOME/mackup/applications/` or `~/.config/mackup/applications/` If the same application config exists in both locations, the legacy location (`~/.mackup/`) takes priority. For XDG-compliant setups, you can use: ```bash mkdir -p ~/.config/mackup/applications touch ~/.config/mackup/applications/nethack.cfg ``` Edit those files: ```bash $ nano ~/.mackup/nethack.cfg [application] name = Nethack [configuration_files] .nethackrc ``` ```bash $ nano ~/.mackup/my-files.cfg [application] name = My personal synced files and dirs [configuration_files] bin .hidden .gitignore ``` Note that Mackup assumes the file paths listed here are relative to your home directory. You can run mackup to see if they are listed: ```bash $ mackup list Supported applications: [...] - my-files - nethack [...] ``` All good, you can now sync your newly configured files: ```bash mackup backup ``` If you override an application config that is already supported by Mackup, your new config for this application will replace the one provided by Mackup. You can find some sample configs in this directory. ### Locally test an application before submitting a Pull Request You can add and test an application by following these steps: - fork this project - create a branch _(usually containing the name of the application)_ - add the appropriate application config file in the `mackup/applications` folder - from the top-most folder _(mackup)_ run `make develop` that replaces the currently installed mackup with the local modified one - simply run `mackup backup` to test if everything is ok - if everything works as expected: - run `make undevelop` to revert to the official version - commit and push the change to your fork and then create the Pulls Request ### Add support for an application using the XDG directory For applications storing their configuration under the `~/.config` folder, you should not hardcode it. The `.config` folder is the default location but it can be named differently on other users' systems by setting the `XDG_CONFIG_HOME` environment variable. See Mackup supports this mechanism and provides a dedicated `xdg_configuration_files` section for those applications. If any path starts with `.config`, remove the `.config` part and move the path to a dedicated `xdg_configuration_files` section. Instead of: ```ini [application] name = Git [configuration_files] .gitconfig .config/git/config .config/git/ignore .config/git/attributes ``` Use this: ```ini [application] name = Git [configuration_files] .gitconfig [xdg_configuration_files] git/config git/ignore git/attributes ``` ================================================ FILE: doc/configuration_merge_guide.md ================================================ # Guide for Merging Configurations Across Machines This guide is for users who want to combine configuration settings for two or more machines. For example, you might have some bash settings on Machine A (like useful command aliases) that are not on Machine B. You also have some settings on Machine B that you would like to move over to Machine A. The problem is that if you use Mackup to send Machine A's bash configuration settings to Machine B, you will permanently lose any configurations on Machine B that you wanted to keep. Mackup obviously has no idea which features you want to keep and which ones you don't, so you'll have to do a bit of work to merge the different configuration files yourself before using Mackup. ## Step 0: Read Through This Entire Guide First It will probably save you some pain in the long run. ## Step 1: Determine Which Configuration Files to Merge First, pick the app you wish to keep in sync. Then determine which configuration files will be synced for that application by doing the following: 1. [Install Mackup](./../INSTALL.md) 2. Create a `.mackup.cfg` file in your home directory 3. Add the following two lines to `.mackup.cfg`. Replace **bash** in the example below with the name of your application. ```text [applications_to_sync] bash ``` You can get a list of supported apps by running `mackup list`. 1. Save the file 2. Run the following command: `mackup --dry-run --verbose backup` This command will let you see what mackup will do behind the scenes when it backs up your application's configuration files so you can readily see what configuration files Mackup will sync. Make note of these files. ## Step 2: Prepare Your Workstations for Syncing Now that you've identified which files you have to merge, choose one of the two approaches below for merging the configuration files. **Method 1** has you do all the configuration file merges first and then pushes them out with Mackup. With **Method 2**, you'll push out the configuration files from one machine to the others and then merge in your configuration changes gradually over time. Which method should you use? It doesn't really matter. Method 1 is more work up front in exchange for less work later. Method 2 is less work up front but more work later. Method 1 is probably best if you have very dissimilar configurations and have few machines. Method 2 is probably best if you have a machine that is very close to working the way you want and just need a few configuration settings from other machines. ### Method 1: Backup Merge-Push Approach 1. Create a backup of each machine's configuration files for the app you wish to sync. 2. Choose a machine that will serve as the initial "master". It doesn't really matter which one. 3. Edit your configuration files on the master machine so that they represent the ideal version of the file you wish to distribute out to your other machines. #### Method 1 Example Let's say we have two machines, A and B, and we want to sync our bash configuration across the machines. We decide that Machine A will serve as our master. First, backup the bash configuration files (there are a few of them) for your application on all machines. ##### Method 1: Sample backup commands for Machine A ```bash mkdir ~/bash_backup cp ~/.bash_profile ~/bash_backup/bash_profile.bak cp ~/.bash_login ~/bash_backup/bash_login.bak ...plus any other bash config files you want to keep ``` ##### Method 1: Sample backup commands for Machine B ```bash mkdir ~/bash_backup cp ~/.bash_profile ~/bash_backup/bash_profile.bak cp ~/.bash_login ~/bash_backup/bash_login.bak ...plus any other bash config files you want to keep ``` Machine A will be our master so we now edit the existing configuration files on Machine A. We will use the vim text editor to do this for each of our configuration files: ```bash vim .bash_profile vim .bash_login ``` When editing these configuration files on Machine A, copy and paste the settings from Machine B that you want to keep. In essence, you are manually merging the configuration files together. Once you are satisfied the configuration files have all the settings you want and need, you are ready to push out your changes from the master machine. ### Method 2: Backup Push-Merge Approach 1. Choose a machine that will serve as the initial "master". You'll probably want to choose the machine you use most and like its configuration settings the best. 2. For each machine that isn't the "master" (i.e. "slaves"), back up all the configuration files for each app that you want to sync. That's it for now. However, there will be more work for you later. #### Method 2 Example Let's say we have two machines, A and B, and we want to sync our bash configuration across the machines. We decide that Machine A will serve as our master. Since A is our master, we only need to backup the bash configuration files on Machine B: ##### Method 2: Sample backup commands for Machine B ```bash mkdir ~/bash_backup cp ~/.bash_profile ~/bash_backup/bash_profile.bak cp ~/.bash_login ~/bash_backup/bash_login.bak ...plus any other bash config files you want to keep ``` If you have other machines you are syncing with the master, back those up, too. ## Step 3: Push Out the Configuration Files with Mackup Now you are ready to use Mackup to push out the changes. You should have Mackup already installed and the `.mackup.cfg` file in place according to the instructions provided above. If not, do that before proceeding. Run the following command on the master machine: `mackup backup` On each of the other "slave" machines, run: `mackup restore` If you used Method 1 in Step 2 above, you are done. You may discover that you didn't quite merge the files exactly the way you wanted but don't worry, that's why you created the configuration file backups. You can grab snippets from these backup configuration files and add them in to the live configuration files and then easily push the changes out to all your machines using mackup. If you used Method 2, you'll need to merge in new features over time. As you discover features you need to add, you'll need to take the appropriate snippets of configuration code from the backup configuration files you created and insert them into the appropriate configuration file. Remember it does not matter which machine's configuration file you update as these configuration files are now shared across all machines. ================================================ FILE: doc/develop.md ================================================ # Develop ```sh # Install the tool for dependency management and packaging in Python pipx install uv # You can now edit files and see the impact of your changes uv run mackup --version make test ``` ## Running Tests with Coverage To run tests with coverage reporting: ```sh # Run tests with coverage make coverage # View coverage report in terminal make coverage-report # Open HTML coverage report open htmlcov/index.html ``` ## Code Quality Checks The project includes several code quality tools: ```sh # Run all checks (ruff, mypy, pytest) make check # Run individual checks make ruff # Code linting make mypy # Type checking make test # Unit tests make coverage # Tests with coverage ``` ## Coverage Configuration Coverage is configured in `pyproject.toml` with: - Branch coverage enabled - Test files excluded from coverage - HTML and XML reports generated - 67%+ coverage currently achieved You can view detailed coverage reports by running `make coverage` and opening `htmlcov/index.html` in your browser. ================================================ FILE: doc/release.md ================================================ # Release 1. Increment the version in [pyproject.toml](../pyproject.toml) 1. Run `uv sync -U` 1. `git commit` with the message `Mackup X.Y.Z` 1. `git tag ` 1. `git push` 1. `git push --tags` 1. Do a release at 1. `make release` ================================================ FILE: doc/syncing-private-keys.md ================================================ # Syncing private keys By default private keys for OpenSSH and GnuPG are NOT sycned. You can sync your private keys if you want. For example, to sync your entire OpenSSH `.ssh` directory, create a `~/.mackup/ssh.cfg` file with the following content: ```ini [application] name = SSH [configuration_files] .ssh ``` ================================================ FILE: pyproject.toml ================================================ [project] name = "mackup" version = "0.10.2" description = "Backup and keep your application settings in sync." readme = "README.md" dependencies = [ "docopt-ng>=0.9.0", ] requires-python = ">= 3.9" authors = [ { name = "Laurent Raufaste", email = "analogue@glop.org" } ] license = "GPL-3.0-or-later" license-files = ["LICENSE"] classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)", "Operating System :: MacOS :: MacOS X", "Operating System :: POSIX :: Linux", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Programming Language :: Python :: 3 :: Only", "Topic :: System :: Archiving :: Backup", "Topic :: Utilities", ] [project.scripts] mackup = "mackup.main:main" [project.urls] Repository = "https://github.com/lra/mackup.git" Issues = "https://github.com/lra/mackup/issues" Changelog = "https://github.com/lra/mackup/releases" [dependency-groups] dev = [ "pytest>=8.4.2", "pytest-cov>=6.0.0", "coverage[toml]>=7.6.0", "mypy>=1.13.0", "ruff>=0.14.4", "ty>=0.0.16", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" [tool.mypy] python_version = "3.9" warn_return_any = true warn_unused_configs = true disallow_untyped_defs = true disallow_incomplete_defs = true check_untyped_defs = true no_implicit_optional = true warn_redundant_casts = true warn_unused_ignores = true warn_no_return = true warn_unreachable = true strict_equality = true show_error_codes = true [tool.coverage.run] source = ["src/mackup"] branch = true omit = [ "*/tests/*", "*/__pycache__/*", "*/.venv/*", ] [tool.coverage.report] precision = 2 show_missing = true skip_covered = false exclude_lines = [ "pragma: no cover", "def __repr__", "raise AssertionError", "raise NotImplementedError", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "class .*\\bProtocol\\):", "@(abc\\.)?abstractmethod", ] [tool.coverage.html] directory = "htmlcov" [tool.pytest] strict = true minversion = "9.0" testpaths = ["tests"] pythonpath = ["src"] addopts = ["-ra"] # https://docs.astral.sh/ruff/rules/ [tool.ruff.lint] select = [ "A", # flake8-builtins "ASYNC", # flake8-async "B", # flake8-bugbear "C4", # flake8-comprehensions "C90", # mccabe "COM", # flake8-commas "DTZ", # flake8-datetimez "E", # pycodestyle error "EXE", # flake8-executable "F", # pyflakes "FA", # flake8-future-annotations "FLY", # flynt, for f-strings "FURB", # refurb "G", # flake8-logging-format "I", # isort "ICN", # flake8-import-conventions "INP", # flake8-no-pep420 "INT", # flake8-gettext "N", # pep8-naming "PERF", # perflint "PIE", # flake8-pie "PLC", # pylint convention "PLE", # pylint error "PLR", # pylint refactor "PLW", # pylint warning "PT", # flake8-pytest-style "PYI", # flake8-pyi "Q", # flake8-quotes "RSE", # flake8-raise "RUF", # ruff "SIM", # flake8-simplify "SLF", # flake8-self "SLOT", # flake8-slots "TCH", # flake8-type-checking "TID", # flake8-tidy-imports "UP", # pyupgrade "W", # pycodestyle warning ] ignore = [ "C901", # mccabe complexity - too complex functions "FA100", # future annotations - would change behavior "PLR0912", # too many branches "PLR0915", # too many statements "PTH", # use pathlib - os.path is fine for this codebase "SIM115", # use context manager for open - some cases require this "SLF001", # private member access - needed for testing ] ================================================ FILE: snap/snapcraft.yaml ================================================ name: mackup base: core24 adopt-info: mackup title: Mackup summary: Keep your application settings in sync description: | Back ups your application settings in a safe directory (e.g. Dropbox). Syncs your application settings among all your workstations. Restores your configuration on any fresh install in one command line. license: GPL-3.0-or-later contact: analogue@glop.org website: https://github.com/lra/mackup source-code: https://github.com/lra/mackup issues: https://github.com/lra/mackup/issues grade: stable confinement: classic apps: mackup: command: bin/mackup parts: mackup: plugin: python source: . stage-packages: - python3.12-minimal - libpython3.12-minimal - libpython3.12-stdlib override-pull: | craftctl default VERSION=$(python3 -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])') craftctl set version="$VERSION" ================================================ FILE: src/mackup/__init__.py ================================================ """The Mackup Package.""" ================================================ FILE: src/mackup/application.py ================================================ """ Application Profile. An Application Profile contains all the information about an application in Mackup. Name, files, ... """ import os from . import utils from .mackup import Mackup class ApplicationProfile: """Instantiate this class with application specific data.""" def __init__( self, mackup: Mackup, files: set[str], dry_run: bool, verbose: bool, ) -> None: """ Create an ApplicationProfile instance. Args: mackup (Mackup) files (list) """ assert isinstance(mackup, Mackup) assert isinstance(files, set) self.mackup: Mackup = mackup self.files: list[str] = sorted(files) self.dry_run: bool = dry_run self.verbose: bool = verbose def get_filepaths(self, filename: str) -> tuple[str, str]: """ Get home and mackup filepaths for given file Args: filename (str) Returns: home_filepath, mackup_filepath (str, str) """ return ( os.path.join(os.environ["HOME"], filename), os.path.join(self.mackup.mackup_folder, filename), ) def copy_files_to_mackup_folder(self) -> None: """ Backup the application config files to the Mackup folder. Algorithm: for config_file if config_file exists and is a real file/folder if home/file is a symlink pointing to mackup/file skip (already backed up via link install) if exists mackup/file are you sure? if sure rm mackup/file cp home/file mackup/file """ for filename in self.files: (home_filepath, mackup_filepath) = self.get_filepaths(filename) # If config_file exists and is a real file/folder if (os.path.isfile(home_filepath) or os.path.isdir(home_filepath)): # Check if home file is a symlink pointing to mackup file # (already backed up via link install) if ( os.path.islink(home_filepath) and os.path.exists(mackup_filepath) and os.path.samefile(home_filepath, mackup_filepath) ): if self.verbose: print( f"Skipping {home_filepath}\n" f" already linked to\n {mackup_filepath}", ) continue if self.verbose: print( f"Backing up\n {home_filepath}\n to\n {mackup_filepath} ...", ) else: print(f"Backing up {filename} ...") if self.dry_run: continue # If exists mackup/file if os.path.lexists(mackup_filepath): # Name it right file_type: str if os.path.isfile(mackup_filepath): file_type = "file" elif os.path.isdir(mackup_filepath): file_type = "folder" elif os.path.islink(mackup_filepath): file_type = "link" else: raise ValueError(f"Unsupported file: {mackup_filepath}") # Ask the user if he really wants to replace it if utils.confirm( f"A {file_type} named {mackup_filepath} already exists in the" " Mackup folder.\nAre you sure that you want to" " replace it? (use --force to skip this prompt)", ): # If confirmed, delete the file in Mackup utils.delete(mackup_filepath) else: continue # Copy the file try: utils.copy(home_filepath, mackup_filepath) except PermissionError as e: print( f"Error: Unable to copy file from {home_filepath} to " f"{mackup_filepath} due to permission issue: {e}", ) def copy_files_from_mackup_folder(self) -> None: """ Recover the application config files from the Mackup folder. Algorithm: for config_file if config_file exists in mackup and is a real file/folder if exists home/file are you sure? if sure rm home/file cp mackup/file home/file """ for filename in self.files: (home_filepath, mackup_filepath) = self.get_filepaths(filename) # If config_file exists in mackup and is a real file/folder if (os.path.isfile(mackup_filepath) or os.path.isdir(mackup_filepath)): if self.verbose: print( f"Recovering\n {mackup_filepath}\n to\n {home_filepath} ...", ) else: print(f"Recovering {filename} ...") if self.dry_run: continue # If exists home/file if os.path.lexists(home_filepath): # Name it right if os.path.isfile(home_filepath): file_type = "file" elif os.path.isdir(home_filepath): file_type = "folder" elif os.path.islink(home_filepath): file_type = "link" else: raise ValueError(f"Unsupported file: {home_filepath}") # Ask the user if he really wants to replace it if utils.confirm( f"A {file_type} named {home_filepath} already exists in your" " home folder.\nAre you sure that you want to" " replace it?", ): # If confirmed, delete the existing home file utils.delete(home_filepath) else: continue # Copy the file try: utils.copy(mackup_filepath, home_filepath) except PermissionError as e: print( f"Error: Unable to copy file from {mackup_filepath} to " f"{home_filepath} due to permission issue: {e}", ) def link_install(self) -> None: """ Create the application config file links. Algorithm: if exists home/file if home/file is a real file if exists mackup/file are you sure? if sure rm mackup/file mv home/file mackup/file link mackup/file home/file else mv home/file mackup/file link mackup/file home/file """ # For each file used by the application for filename in self.files: (home_filepath, mackup_filepath) = self.get_filepaths(filename) # If the file exists and is not already a link pointing to Mackup if (os.path.isfile(home_filepath) or os.path.isdir(home_filepath)) and not ( os.path.islink(home_filepath) and (os.path.isfile(mackup_filepath) or os.path.isdir(mackup_filepath)) and os.path.samefile(home_filepath, mackup_filepath) ): if self.verbose: print( f"Backing up\n {home_filepath}\n to\n {mackup_filepath} ...", ) else: print(f"Linking {filename} ...") if self.dry_run: continue # Check if we already have a backup if os.path.exists(mackup_filepath): # Name it right if os.path.isfile(mackup_filepath): file_type = "file" elif os.path.isdir(mackup_filepath): file_type = "folder" elif os.path.islink(mackup_filepath): file_type = "link" else: raise ValueError(f"Unsupported file: {mackup_filepath}") # Ask the user if he really wants to replace it if utils.confirm( f"A {file_type} named {mackup_filepath} already exists in the" " backup.\nAre you sure that you want to" " replace it?", ): # Delete the file in Mackup utils.delete(mackup_filepath) # Copy the file utils.copy(home_filepath, mackup_filepath) # Delete the file in the home utils.delete(home_filepath) # Link the backuped file to its original place utils.link(mackup_filepath, home_filepath) else: # Copy the file utils.copy(home_filepath, mackup_filepath) # Delete the file in the home utils.delete(home_filepath) # Link the backuped file to its original place utils.link(mackup_filepath, home_filepath) elif self.verbose: if os.path.exists(home_filepath): print( f"Doing nothing\n {home_filepath}\n " f"is already backed up to\n {mackup_filepath}", ) elif os.path.islink(home_filepath): print( f"Doing nothing\n {home_filepath}\n " "is a broken link, you might want to fix it.", ) else: print(f"Doing nothing\n {home_filepath}\n does not exist") def link(self) -> None: """ Link the application config files. Algorithm: if exists mackup/file if exists home/file are you sure? if sure rm home/file link mackup/file home/file else link mackup/file home/file """ # For each file used by the application for filename in self.files: (home_filepath, mackup_filepath) = self.get_filepaths(filename) # If the file exists and is not already pointing to the mackup file # and the folder makes sense on the current platform (Don't sync # any subfolder of ~/Library on GNU/Linux) file_or_dir_exists: bool = os.path.isfile(mackup_filepath) or os.path.isdir( mackup_filepath, ) pointing_to_mackup: bool = ( os.path.islink(home_filepath) and os.path.exists(mackup_filepath) and os.path.samefile(mackup_filepath, home_filepath) ) supported: bool = utils.can_file_be_synced_on_current_platform(filename) if file_or_dir_exists and not pointing_to_mackup and supported: if self.verbose: print( f"Restoring\n linking {home_filepath}\n" f" to {mackup_filepath} ...", ) else: print(f"Restoring {filename} ...") if self.dry_run: continue # Check if there is already a file in the home folder if os.path.exists(home_filepath): # Name it right if os.path.isfile(home_filepath): file_type = "file" elif os.path.isdir(home_filepath): file_type = "folder" elif os.path.islink(home_filepath): file_type = "link" else: raise ValueError(f"Unsupported file: {home_filepath}") if utils.confirm( f"You already have a {file_type} at {home_filepath}.\n" "Do you want to replace it with your backup?", ): utils.delete(home_filepath) utils.link(mackup_filepath, home_filepath) else: utils.link(mackup_filepath, home_filepath) elif self.verbose: if os.path.exists(home_filepath): print( f"Doing nothing\n {mackup_filepath}\n" f" already linked by\n {home_filepath}", ) elif os.path.islink(home_filepath): print( f"Doing nothing\n {home_filepath}\n " "is a broken link, you might want to fix it.", ) else: print( f"Doing nothing\n {mackup_filepath}\n does not exist", ) def link_uninstall(self) -> None: """ Removes links and copy config files from the remote folder locally. Algorithm: for each file in config if mackup/file exists if home/file exists delete home/file copy mackup/file home/file """ # For each file used by the application for filename in self.files: (home_filepath, mackup_filepath) = self.get_filepaths(filename) # If the mackup file exists if os.path.isfile(mackup_filepath) or os.path.isdir(mackup_filepath): # Check if there is a corresponding file in the home folder if os.path.exists(home_filepath): # If the home file is not a link or does not point to the # mackup file, display a warning and skip it. if not os.path.islink(home_filepath) or not os.path.samefile( home_filepath, mackup_filepath, ): print( f'Warning: the file in your home "{home_filepath}" ' f"does not point to the original file in Mackup " f"{mackup_filepath}, skipping...", ) continue if self.verbose: print( f"Reverting {mackup_filepath}\n at {home_filepath} ...", ) else: print(f"Reverting {filename} ...") if self.dry_run: continue # If there is, delete it as we are gonna copy the Dropbox # one there utils.delete(home_filepath) # Copy the Dropbox file to the home folder utils.copy(mackup_filepath, home_filepath) elif self.verbose: print(f"Doing nothing, {mackup_filepath} does not exist") ================================================ FILE: src/mackup/applications/1password-4.cfg ================================================ [application] name = 1Password 4 [configuration_files] Library/Preferences/com.agilebits.onepassword4.plist Library/Preferences/ws.agile.1Password.plist ================================================ FILE: src/mackup/applications/2do.cfg ================================================ [application] name = 2Do [configuration_files] Library/Preferences/com.guidedways.TodoMac.plist ================================================ FILE: src/mackup/applications/ack.cfg ================================================ [application] name = Ack [configuration_files] .ackrc ================================================ FILE: src/mackup/applications/act.cfg ================================================ [application] name = act [configuration_files] .actrc ================================================ FILE: src/mackup/applications/activitywatch.cfg ================================================ [application] name = Activity Watch [configuration_files] Library/Application Support/activitywatch ================================================ FILE: src/mackup/applications/adium.cfg ================================================ [application] name = Adium [configuration_files] Library/Application Support/Adium 2.0 Library/Preferences/com.adiumX.adiumX.plist ================================================ FILE: src/mackup/applications/adobe-camera-raw.cfg ================================================ [application] name = Adobe Camera Raw [configuration_files] Library/Application Support/Adobe/CameraRaw ================================================ FILE: src/mackup/applications/aerc.cfg ================================================ [application] name = aerc [configuration_files] Library/Preferences/aerc ================================================ FILE: src/mackup/applications/aerospace.cfg ================================================ [application] name = aerospace [configuration_files] .aerospace.toml [xdg_configuration_files] aerospace/aerospace.toml ================================================ FILE: src/mackup/applications/affinity-designer.cfg ================================================ [application] name = Affinity Designer [configuration_files] Library/Containers/Affinity Designer/Data/Library/Application Support/user ================================================ FILE: src/mackup/applications/affinity-photo.cfg ================================================ [application] name = Affinity Photo [configuration_files] Library/Containers/Affinity Photo/Data/Library/Application Support/user ================================================ FILE: src/mackup/applications/affinity-publisher.cfg ================================================ [application] name = Affinity Publisher [configuration_files] Library/Containers/Affinity Publisher/Data/Library/Application Support/user ================================================ FILE: src/mackup/applications/airflow.cfg ================================================ [application] name = Airflow [configuration_files] Library/Preferences/com.bitcavehq.Airflow.plist Library/Application Support/Airflow ================================================ FILE: src/mackup/applications/airmail.cfg ================================================ [application] name = Airmail [configuration_files] Library/Containers/it.bloop.airmail2/Container.plist ================================================ FILE: src/mackup/applications/akamai-cli.cfg ================================================ [application] name = Akamai-CLI [configuration_files] .edgerc ================================================ FILE: src/mackup/applications/alacritty.cfg ================================================ [application] name = Alacritty [configuration_files] .alacritty.yml .alacritty.toml [xdg_configuration_files] alacritty/alacritty.yml alacritty/alacritty.toml ================================================ FILE: src/mackup/applications/aldente.cfg ================================================ [application] name = AlDente [configuration_files] Library/Application Support/AlDente Library/Preferences/com.apphousekitchen.aldente-pro.plist ================================================ FILE: src/mackup/applications/alt-tab.cfg ================================================ [application] name = AltTab [configuration_files] Library/Preferences/com.lwouis.alt-tab-macos.plist ================================================ FILE: src/mackup/applications/amethyst.cfg ================================================ [application] name = Amethyst [configuration_files] Library/Preferences/com.amethyst.Amethyst.plist [xdg_configuration_files] amethyst/amethyst.yml ================================================ FILE: src/mackup/applications/ancient-domains-of-mystery.cfg ================================================ [application] name = Ancient Domains of Mystery [configuration_files] .adom.data ================================================ FILE: src/mackup/applications/androidstudio-13.cfg ================================================ [application] name = Android Studio 1.3 [configuration_files] Library/Application Support/AndroidStudio1.3 Library/Preferences/AndroidStudio1.3 ================================================ FILE: src/mackup/applications/ansible.cfg ================================================ [application] name = Ansible [configuration_files] .ansible .ansible.cfg ================================================ FILE: src/mackup/applications/appcleaner.cfg ================================================ [application] name = AppCleaner [configuration_files] Library/Preferences/net.freemacsoft.AppCleaner.plist Library/Preferences/net.freemacsoft.AppCleaner-SmartDelete.plist ================================================ FILE: src/mackup/applications/appcode-2.cfg ================================================ [application] name = AppCode 2 [configuration_files] Library/Application Support/appCode20 Library/Preferences/appCode20 ================================================ FILE: src/mackup/applications/appcode-3.cfg ================================================ [application] name = AppCode 3 [configuration_files] Library/Application Support/appCode30 Library/Preferences/appCode30 ================================================ FILE: src/mackup/applications/appcode-31.cfg ================================================ [application] name = AppCode 3.1 [configuration_files] Library/Application Support/appCode31 Library/Preferences/appCode31 ================================================ FILE: src/mackup/applications/appcode-32.cfg ================================================ [application] name = AppCode 3.2 [configuration_files] Library/Application Support/AppCode32 Library/Preferences/AppCode32 ================================================ FILE: src/mackup/applications/apple-music.cfg ================================================ [application] name = Apple Music [configuration_files] Library/Music/Scripts Library/Preferences/com.apple.Music.eq.plist Library/Preferences/com.apple.Music.plist ================================================ FILE: src/mackup/applications/apptivate.cfg ================================================ [application] name = Apptivate [configuration_files] Library/Application Support/Apptivate/hotkeys ================================================ FILE: src/mackup/applications/arara.cfg ================================================ [application] name = Arara [configuration_files] .araraconfig.yaml araraconfig.yaml ================================================ FILE: src/mackup/applications/aria2c.cfg ================================================ [application] name = aria2c [configuration_files] .aria2 [xdg_configuration_files] aria2 ================================================ FILE: src/mackup/applications/arm.cfg ================================================ [application] name = Arm [configuration_files] .arm/armrc ================================================ FILE: src/mackup/applications/asciinema.cfg ================================================ [application] name = asciinema [xdg_configuration_files] asciinema ================================================ FILE: src/mackup/applications/asdf.cfg ================================================ [application] name = asdf [configuration_files] .asdfrc .default-gems .default-npm-packages .default-python-packages .default-golang-pkgs .default-mix-commands .tool-versions ================================================ FILE: src/mackup/applications/aspell.cfg ================================================ [application] name = Aspell [configuration_files] .aspell.conf .aspell.en.prepl .aspell.en.pws ================================================ FILE: src/mackup/applications/astyle.cfg ================================================ [application] name = Artistic Style [configuration_files] .astylerc ================================================ FILE: src/mackup/applications/atlantis.cfg ================================================ [application] name = Atlantis [configuration_files] Library/Application Support/Atlantis ================================================ FILE: src/mackup/applications/atom.cfg ================================================ [application] name = Atom [configuration_files] Library/Preferences/com.github.atom.plist .atom/config.cson .atom/init.coffee .atom/keymap.cson .atom/keymaps .atom/snippets.cson .atom/styles.less ================================================ FILE: src/mackup/applications/audacious.cfg ================================================ [application] name = Audacious [xdg_configuration_files] audacious/config audacious/plugin-registry audacious/playlists audacious/playlist-state audacious/scrobbler.log ================================================ FILE: src/mackup/applications/auskey.cfg ================================================ [application] name = AusKey [configuration_files] Library/Application Support/AUSkey/keystore.xml ================================================ FILE: src/mackup/applications/autokey.cfg ================================================ [application] name = Autokey [xdg_configuration_files] autokey ================================================ FILE: src/mackup/applications/awareness.cfg ================================================ [application] name = Awareness [configuration_files] Library/Preferences/com.futureproof.awareness.plist ================================================ FILE: src/mackup/applications/aws.cfg ================================================ [application] name = AWS CLI [configuration_files] .aws ================================================ FILE: src/mackup/applications/azure.cfg ================================================ [application] name = Azure CLI [configuration_files] .azure ================================================ FILE: src/mackup/applications/b-ryan_powerline-shell.cfg ================================================ [application] name = Powerline Shell Prompt [xdg_configuration_files] powerline-shell/config.json ================================================ FILE: src/mackup/applications/bartender.cfg ================================================ [application] name = Bartender [configuration_files] Library/Preferences/com.surteesstudios.Bartender.plist Library/Preferences/com.surteesstudios.Bartender-setapp.plist Library/Application Support/Bartender/Bartender.BartenderPreferences ================================================ FILE: src/mackup/applications/base.cfg ================================================ [application] name = Base [configuration_files] Library/Containers/uk.co.menial.Base/Data/Library/Preferences/uk.co.menial.Base.plist # contains license and preference state Library/Containers/uk.co.menial.Base/Data/Library/Application Support/Base/ ================================================ FILE: src/mackup/applications/bash-it.cfg ================================================ [application] name = Bash it [configuration_files] .bash_it/enabled ================================================ FILE: src/mackup/applications/bash.cfg ================================================ [application] name = Bash [configuration_files] .aliases .bash_aliases .bash_login .bash_logout .bashrc .profile .bash_profile .inputrc ================================================ FILE: src/mackup/applications/bat.cfg ================================================ [application] name = Bat [xdg_configuration_files] bat/config bat/syntaxes bat/themes ================================================ FILE: src/mackup/applications/bc.cfg ================================================ [application] name = bc [configuration_files] .bcrc ================================================ FILE: src/mackup/applications/beatport-pro.cfg ================================================ [application] name = Beatport Pro [configuration_files] Library/Preferences/com.beatport.BeatportPro.plist ================================================ FILE: src/mackup/applications/beets.cfg ================================================ [application] name = Beets [xdg_configuration_files] beets/config.yaml ================================================ FILE: src/mackup/applications/bettersnaptool.cfg ================================================ [application] name = BetterSnapTool [configuration_files] Library/Preferences/com.hegenberg.BetterSnapTool.plist Library/Application Support/BetterSnapTool ================================================ FILE: src/mackup/applications/bettertouchtool.cfg ================================================ [application] name = BetterTouchTool [configuration_files] Library/Application Support/BetterTouchTool/bettertouchtool.license Library/Application Support/BetterTouchTool/btt_data_store.v2 Library/Application Support/BetterTouchTool/btt_data_store.v2-shm Library/Application Support/BetterTouchTool/btt_data_store.v2-wal Library/Application Support/BetterTouchTool/bttdata2 Library/Preferences/com.hegenberg.BetterTouchTool.plist Library/Application Support/BetterTouchTool/bettersnaptool.bttlicense ================================================ FILE: src/mackup/applications/beyond-compare.cfg ================================================ [application] name = Beyond Compare [configuration_files] Library/Preferences/com.ScooterSoftware.BeyondCompare.plist Library/Application Support/Beyond Compare/BC4Key.txt Library/Application Support/Beyond Compare/BCColors.xml Library/Application Support/Beyond Compare/BCCommands.xml Library/Application Support/Beyond Compare/BCPreferences.xml ================================================ FILE: src/mackup/applications/bibdesk.cfg ================================================ [application] name = BibDesk [configuration_files] Library/Preferences/edu.ucsd.cs.mmccrack.bibdesk.plist Library/Application Support/BibDesk ================================================ FILE: src/mackup/applications/billings-pro-server-admin.cfg ================================================ [application] name = Billings Pro Server Admin [configuration_files] Library/Preferences/com.marketcircle.BillingsProServerAdmin.plist ================================================ FILE: src/mackup/applications/bitbar.cfg ================================================ [application] name = BitBar [configuration_files] Library/Preferences/com.matryer.BitBar.plist ================================================ FILE: src/mackup/applications/bitchx.cfg ================================================ [application] name = Bitchx [configuration_files] .bitchxrc .ircservers ================================================ FILE: src/mackup/applications/blackfire.cfg ================================================ [application] name = Blackfire [configuration_files] .blackfire.ini ================================================ FILE: src/mackup/applications/blender.cfg ================================================ [application] name = Blender [configuration_files] Library/Application Support/Blender [xdg_configuration_files] blender ================================================ FILE: src/mackup/applications/blesh.cfg ================================================ [application] name = ble.sh [configuration_files] .blerc ================================================ FILE: src/mackup/applications/boto.cfg ================================================ [application] name = Boto [configuration_files] .boto ================================================ FILE: src/mackup/applications/boxer.cfg ================================================ [application] name = Boxer [configuration_files] Library/Preferences/net.washboardabs.boxer.plist Applications/DOS Games ================================================ FILE: src/mackup/applications/brackets.cfg ================================================ [application] name = Brackets [configuration_files] Library/Application Support/Brackets Library/Preferences/io.brackets.appshell.plist ================================================ FILE: src/mackup/applications/brave.cfg ================================================ [application] name = Brave [configuration_files] Library/Application Support/BraveSoftware/Brave-Browser/Default/Preferences ================================================ FILE: src/mackup/applications/btop.cfg ================================================ [application] name = btop [xdg_configuration_files] btop ================================================ FILE: src/mackup/applications/bump.cfg ================================================ [application] name = Bump [configuration_files] .bump.json ================================================ FILE: src/mackup/applications/bundler.cfg ================================================ [application] name = Bundler [configuration_files] .bundle/config ================================================ FILE: src/mackup/applications/byobu.cfg ================================================ [application] name = Byobu [configuration_files] .byobu/.screenrc .byobu/.tmux.conf .byobu/backend .byobu/color .byobu/color.tmux .byobu/datetime.tmux .byobu/keybindings .byobu/keybindings.tmux .byobu/profile .byobu/profile.tmux .byobu/prompt .byobu/prompt.tmux .byobu/status .byobu/statusrc .byobu/windows .byobu/windows.tmux .byoburc .byoburc.tmux .byoburc.screen ================================================ FILE: src/mackup/applications/caffeine.cfg ================================================ [application] name = Caffeine [configuration_files] Library/Preferences/com.lightheadsw.Caffeine.plist Library/Preferences/com.intelliscapesolutions.caffeine.plist ================================================ FILE: src/mackup/applications/calibre.cfg ================================================ [application] name = Calibre [configuration_files] Library/Preferences/calibre [xdg_configuration_files] calibre ================================================ FILE: src/mackup/applications/capture-one.cfg ================================================ [application] name = Capture One [configuration_files] Library/Application Support/Capture One ================================================ FILE: src/mackup/applications/cartographica.cfg ================================================ [application] name = Cartographica [configuration_files] Library/Application Support/Cartographica Library/Preferences/com.ClueTrust.Cartographica.plist ================================================ FILE: src/mackup/applications/cerebro.cfg ================================================ [application] name = Cerebro [configuration_files] Library/Application Support/Cerebro/config.json Library/Application Support/Cerebro/plugins [xdg_configuration_files] Cerebro/config.json Cerebro/plugins ================================================ FILE: src/mackup/applications/charles.cfg ================================================ [application] name = Charles [configuration_files] Library/Application Support/Charles Library/Preferences/com.xk72.charles.config ================================================ FILE: src/mackup/applications/cheat.cfg ================================================ [application] name = Cheat [configuration_files] .cheat [xdg_configuration_files] cheat/conf.yml ================================================ FILE: src/mackup/applications/chef.cfg ================================================ [application] name = Chef [configuration_files] .chef ================================================ FILE: src/mackup/applications/chicken.cfg ================================================ [application] name = Chicken [configuration_files] Library/Preferences/net.sourceforge.chicken.plist ================================================ FILE: src/mackup/applications/choosy.cfg ================================================ [application] name = Choosy [configuration_files] Library/Application Support/Choosy/behaviours.plist ================================================ FILE: src/mackup/applications/chunkwm.cfg ================================================ [application] name = chunkwm [configuration_files] .chunkwmrc .chunkwm_plugins ================================================ FILE: src/mackup/applications/cider.cfg ================================================ [application] name = Cider [configuration_files] .cider ================================================ FILE: src/mackup/applications/clashx.cfg ================================================ [application] name = ClashX [xdg_configuration_files] clash ================================================ FILE: src/mackup/applications/clasp.cfg ================================================ [application] name = Clasp [configuration_files] .clasprc.json ================================================ FILE: src/mackup/applications/claude-code.cfg ================================================ [application] name = Claude Code [configuration_files] .claude/agents .claude/CLAUDE.md .claude/hooks .claude/keybindings.json .claude/plugins/blocklist.json .claude/plugins/config.json .claude/plugins/installed_plugins.json .claude/plugins/known_marketplaces.json .claude/settings.json .claude/skills .claude/statusline.sh ================================================ FILE: src/mackup/applications/cleanshot.cfg ================================================ [application] name = CleanShot [configuration_files] Library/Preferences/com.getcleanshot.app-setapp.plist Library/Preferences/pl.maketheweb.cleanshotx.plist ================================================ FILE: src/mackup/applications/clementine.cfg ================================================ [application] name = Clementine [configuration_files] Library/Preferences/org.clementine-player.Clementine.plist ================================================ FILE: src/mackup/applications/clion.cfg ================================================ [application] name = CLion [configuration_files] Library/Preferences/CLion2016.2 Library/Application Support/CLion2016.2 Library/Application Support/JetBrains/CLion2023.1 Library/Application Support/JetBrains/CLion2023.2 ================================================ FILE: src/mackup/applications/clipmenu.cfg ================================================ [application] name = ClipMenu [configuration_files] Library/Application Support/ClipMenu Library/Preferences/com.naotaka.ClipMenu.plist ================================================ FILE: src/mackup/applications/clipy.cfg ================================================ [application] name = Clipy [configuration_files] Library/Preferences/com.clipy-app.Clipy.plist ================================================ FILE: src/mackup/applications/cloudapp.cfg ================================================ [application] name = CloudApp [configuration_files] Library/Preferences/com.linebreak.CloudAppMacOSX.plist ================================================ FILE: src/mackup/applications/coda-2.cfg ================================================ [application] name = Coda 2 [configuration_files] Library/Application Support/Coda 2 Library/Preferences/com.panic.Coda2.plist ================================================ FILE: src/mackup/applications/codex.cfg ================================================ [application] name = Codex [configuration_files] .codex/config.toml .codex/auth.json .codex/AGENTS.md ================================================ FILE: src/mackup/applications/colloquy.cfg ================================================ [application] name = Colloquy [configuration_files] Library/Preferences/info.colloquy.plist Library/Application Support/Colloquy ================================================ FILE: src/mackup/applications/colorschemer-studio-2.cfg ================================================ [application] name = ColorSchemer Studio 2 [configuration_files] Library/Preferences/com.colorschemer.studio2.plist Library/Application Support/ColorSchemer ================================================ FILE: src/mackup/applications/colorslurp.cfg ================================================ [application] name = ColorSlurp [configuration_files] Library/Preferences/com.IdeaPunch.ColorSlurp.plist Library/Containers/com.IdeaPunch.ColorSlurp/Data/Library/Application Support/default.realm ================================================ FILE: src/mackup/applications/colorsync.cfg ================================================ [application] name = ColorSync [configuration_files] Library/ColorSync/Profiles ================================================ FILE: src/mackup/applications/composer.cfg ================================================ [application] name = composer [configuration_files] .composer/auth.json .composer/config.json .composer/composer.json ================================================ FILE: src/mackup/applications/concentrate.cfg ================================================ [application] name = Concentrate [configuration_files] Library/Application Support/Concentrate/Concentrate.sqlite3 ================================================ FILE: src/mackup/applications/conky.cfg ================================================ [application] name = Conky [configuration_files] .conkyrc ================================================ FILE: src/mackup/applications/consular.cfg ================================================ [application] name = Consular [configuration_files] .consularc [xdg_configuration_files] consular ================================================ FILE: src/mackup/applications/contexts.cfg ================================================ [application] name = Contexts [configuration_files] Library/Preferences/com.contextsformac.Contexts.plist ================================================ FILE: src/mackup/applications/controlplane.cfg ================================================ [application] name = ControlPlane [configuration_files] Library/Preferences/com.dustinrue.ControlPlane.plist ================================================ FILE: src/mackup/applications/copyq.cfg ================================================ [application] name = CopyQ [xdg_configuration_files] copyq ================================================ FILE: src/mackup/applications/cord.cfg ================================================ [application] name = CoRD [configuration_files] Library/Application Support/CoRD ================================================ FILE: src/mackup/applications/coteditor.cfg ================================================ [application] name = CotEditor [configuration_files] Library/Application Support/CotEditor Library/Preferences/com.coteditor.CotEditor.plist ================================================ FILE: src/mackup/applications/ctags.cfg ================================================ [application] name = Ctags [configuration_files] .ctags .ctags.d ================================================ FILE: src/mackup/applications/curl.cfg ================================================ [application] name = Curl [configuration_files] .netrc .curlrc ================================================ FILE: src/mackup/applications/cursor.cfg ================================================ [application] name = Cursor [configuration_files] Library/Application Support/Cursor/User/snippets Library/Application Support/Cursor/User/keybindings.json Library/Application Support/Cursor/User/settings.json [xdg_configuration_files] Cursor/User/snippets Cursor/User/keybindings.json Cursor/User/settings.json ================================================ FILE: src/mackup/applications/cvim.cfg ================================================ [application] name = cVim [configuration_files] .cvimrc ================================================ FILE: src/mackup/applications/cyberduck.cfg ================================================ [application] name = Cyberduck [configuration_files] # based on https://trac.cyberduck.io/wiki/help/en/faq#Preferencesandapplicationsupportfileslocation Library/Application Support/Cyberduck Library/Containers/ch.sudo.cyberduck/Data/Library/Application Support/Cyberduck Library/Group Containers/G69SCX94XU.duck/Library/Application Support/duck Library/Preferences/ch.sudo.cyberduck.plist ================================================ FILE: src/mackup/applications/daisydisk.cfg ================================================ [application] name = DaisyDisk [configuration_files] Library/Application Support/DaisyDisk/License.DaisyDisk Library/Preferences/com.daisydiskapp.DaisyDiskStandAlone.plist ================================================ FILE: src/mackup/applications/dash.cfg ================================================ [application] name = Dash [configuration_files] Library/Application Support/Dash/library.dash Library/Application Support/Dash/License/license.dash-license Library/Preferences/com.kapeli.dash.plist Library/Preferences/com.kapeli.dashdoc.plist ================================================ FILE: src/mackup/applications/datagrip.cfg ================================================ [application] name = DataGrip [configuration_files] Library/Application Support/DataGrip2017.3 Library/Preferences/DataGrip2017.3 Library/Application Support/DataGrip2018.1 Library/Preferences/DataGrip2018.1 Library/Application Support/DataGrip2018.2 Library/Preferences/DataGrip2018.2 Library/Application Support/DataGrip2018.3 Library/Preferences/DataGrip2018.3 Library/Application Support/DataGrip2019.1 Library/Preferences/DataGrip2019.1 Library/Preferences/DataGrip2019.2 Library/Application Support/JetBrains/DataGrip2023.1 Library/Application Support/JetBrains/DataGrip2023.2 ================================================ FILE: src/mackup/applications/day-o.cfg ================================================ [application] name = Day-O [configuration_files] Library/Preferences/com.shauninman.Day-O.plist ================================================ FILE: src/mackup/applications/dbeaver.cfg ================================================ [application] name = DBeaver [configuration_files] Library/DBeaverData/workspace6/.metadata/.plugins/org.eclipse.core.runtime/.settings Library/DBeaverData/workspace6/General ================================================ FILE: src/mackup/applications/dbvisualizer.cfg ================================================ [application] name = DbVisualizer [configuration_files] .dbvis ================================================ FILE: src/mackup/applications/deal-alert.cfg ================================================ [application] name = Deal Alert [configuration_files] Library/Preferences/com.LittleFin.DealAlert.plist ================================================ FILE: src/mackup/applications/deepin-dde-dock.cfg ================================================ [application] name = deepin-dde-dock [xdg_configuration_files] deepin/dde-dock.conf ================================================ FILE: src/mackup/applications/deepin-dde-file-manager.cfg ================================================ [application] name = deepin-dde-file-manager [xdg_configuration_files] deepin/dde-file-manager.json ================================================ FILE: src/mackup/applications/deepin-terminal.cfg ================================================ [application] name = deepin-terminal [xdg_configuration_files] deepin/deepin-terminal/config.conf ================================================ FILE: src/mackup/applications/default-folder-x.cfg ================================================ [application] name = Default Folder X [configuration_files] Library/Preferences/com.stclairsoft.DefaultFolderX.favorites.plist Library/Preferences/com.stclairsoft.DefaultFolderX.plist Library/Preferences/com.stclairsoft.DefaultFolderX.settings.plist ================================================ FILE: src/mackup/applications/defaultkeybinding.cfg ================================================ [application] name = DefaultKeyBinding [configuration_files] Library/KeyBindings/DefaultKeyBinding.dict ================================================ FILE: src/mackup/applications/devilspie.cfg ================================================ [application] name = Devil's Pie [configuration_files] .devilspie ================================================ FILE: src/mackup/applications/devilspie2.cfg ================================================ [application] name = Devilspie2 [xdg_configuration_files] devilspie2 ================================================ FILE: src/mackup/applications/dig.cfg ================================================ [application] name = dig [configuration_files] .digrc ================================================ FILE: src/mackup/applications/divvy.cfg ================================================ [application] name = Divvy [configuration_files] Library/Preferences/com.mizage.direct.Divvy.plist Library/Preferences/com.mizage.Divvy.plist ================================================ FILE: src/mackup/applications/docker.cfg ================================================ [application] name = Docker [configuration_files] .docker/config.json .docker/daemon.json ================================================ FILE: src/mackup/applications/dolphin.cfg ================================================ [application] name = Dolphin [configuration_files] Library/Application Support/Dolphin Library/Preferences/org.dolphin-emu.dolphin.plist ================================================ FILE: src/mackup/applications/doom-emacs.cfg ================================================ [application] name = Doom Emacs [configuration_files] .doom.d [xdg_configuration_files] doom ================================================ FILE: src/mackup/applications/doublecmd.cfg ================================================ [application] name = Double Commander [xdg_configuration_files] doublecmd/wfx.ini doublecmd/doublecmd.ext doublecmd/shortcuts.scf doublecmd/doublecmd.xml ================================================ FILE: src/mackup/applications/doxie.cfg ================================================ [application] name = Doxie [configuration_files] Library/Preferences/com.getdoxie.doxie.plist ================================================ FILE: src/mackup/applications/dozer.cfg ================================================ [application] name = Dozer [configuration_files] Library/Preferences/com.mortennn.Dozer.plist ================================================ FILE: src/mackup/applications/draft.cfg ================================================ [application] name = Draft [configuration_files] .draft/config.toml ================================================ FILE: src/mackup/applications/droplr.cfg ================================================ [application] name = Droplr [configuration_files] Library/Preferences/com.droplr.droplr-mac.plist ================================================ FILE: src/mackup/applications/dropzone.cfg ================================================ [application] name = Dropzone 3 [configuration_files] Library/Application Support/Dropzone 3 Library/Preferences/com.aptonic.Dropzone3.plist ================================================ FILE: src/mackup/applications/drush.cfg ================================================ [application] name = Drush [configuration_files] .drush ================================================ FILE: src/mackup/applications/editorconfig.cfg ================================================ [application] name = EditorConfig [configuration_files] .editorconfig ================================================ FILE: src/mackup/applications/electrum.cfg ================================================ [application] name = Electrum [configuration_files] .electrum/config ================================================ FILE: src/mackup/applications/emacs.cfg ================================================ [application] name = Emacs [configuration_files] .emacs .emacs.d [xdg_configuration_files] emacs ================================================ FILE: src/mackup/applications/enjoyable.cfg ================================================ [application] name = Enjoyable [configuration_files] Library/Preferences/com.yukkurigames.Enjoyable.plist ================================================ FILE: src/mackup/applications/environmental-station-alpha.cfg ================================================ [application] name = Environmental Station Alpha [configuration_files] Library/Application Support/MMFApplications ================================================ FILE: src/mackup/applications/eqmac-2.cfg ================================================ [application] name = eqMac2 [configuration_files] Library/Preferences/com.bitgapp.eqMac2.plist ================================================ FILE: src/mackup/applications/eslint.cfg ================================================ [application] name = ESLint [configuration_files] .eslintrc.js .eslintrc.yaml .eslintrc.yml .eslintrc.json .eslintrc .eslintignore ================================================ FILE: src/mackup/applications/espanso.cfg ================================================ [application] name = espanso [configuration_files] Library/Preferences/espanso Library/Application Support/espanso [xdg_configuration_files] espanso ================================================ FILE: src/mackup/applications/exercism.cfg ================================================ [application] name = Exercism [configuration_files] .exercism .exercism.json [xdg_configuration_files] exercism ================================================ FILE: src/mackup/applications/expandrive.cfg ================================================ [application] name = ExpanDrive [configuration_files] Library/Application Support/ExpanDrive Preferences/com.expandrive.ExpanDrive2.plist Preferences/com.expandrive.ExpanDrive3.plist ================================================ FILE: src/mackup/applications/factorio.cfg ================================================ [application] name = Factorio [configuration_files] Library/Application Support/factorio/config Library/Application Support/factorio/mods Library/Application Support/factorio/saves Library/Application Support/factorio/player-data.json Library/Application Support/factorio/blueprint-storage.dat ================================================ FILE: src/mackup/applications/factory-droid.cfg ================================================ [application] name = Factory Droid [configuration_files] .factory/commands .factory/droids .factory/AGENTS.md .factory/config.json .factory/settings.json .factory/auth.json .factory/mcp.json ================================================ FILE: src/mackup/applications/fantastical.cfg ================================================ [application] name = Fantastical [configuration_files] # Used by Fantastical 1 only. Library/Preferences/com.flexibits.fantastical.plist # Used by Fantastical 2 only. Library/Containers/com.flexibits.fantastical2.mac/Container.plist ================================================ FILE: src/mackup/applications/fasd.cfg ================================================ [application] name = fasd [configuration_files] .fasd .fasdrc ================================================ FILE: src/mackup/applications/fastlane.cfg ================================================ [application] name = fastlane [configuration_files] .fastlane/completions ================================================ FILE: src/mackup/applications/fastscripts.cfg ================================================ [application] name = FastScripts [configuration_files] Library/Preferences/com.red-sweater.fastscripts.plist Library/Application Support/FastScripts ================================================ FILE: src/mackup/applications/feeds.cfg ================================================ [application] name = Feeds [configuration_files] Library/Preferences/com.feedsapp.Feeds.plist ================================================ FILE: src/mackup/applications/filezilla.cfg ================================================ [application] name = FileZilla [configuration_files] .filezilla [xdg_configuration_files] filezilla/filezilla.xml filezilla/layout.xml filezilla/sitemanager.xml ================================================ FILE: src/mackup/applications/finicky.cfg ================================================ [application] name = finicky [configuration_files] .finicky.js ================================================ FILE: src/mackup/applications/fish.cfg ================================================ [application] name = Fish [xdg_configuration_files] fish/config.fish fish/conf.d fish/fish_variables fish/functions fish/completions ================================================ FILE: src/mackup/applications/fisher.cfg ================================================ [application] name = Fisher [xdg_configuration_files] fish/fish_plugins ================================================ FILE: src/mackup/applications/flake8.cfg ================================================ [application] name = flake8 [configuration_files] .flake8 [xdg_configuration_files] flake8 ================================================ FILE: src/mackup/applications/flameshot.cfg ================================================ [application] name = Flameshot [xdg_configuration_files] flameshot/flameshot.ini ================================================ FILE: src/mackup/applications/flexget.cfg ================================================ [application] name = FlexGet [configuration_files] .flexget/config.yml [xdg_configuration_files] flexget/config.yml ================================================ FILE: src/mackup/applications/flux.cfg ================================================ [application] name = Flux [configuration_files] Library/Preferences/org.herf.Flux.plist ================================================ FILE: src/mackup/applications/focus.cfg ================================================ [application] name = Focus [configuration_files] Library/Application Support/Focus/default.cfg ================================================ FILE: src/mackup/applications/fontconfig.cfg ================================================ [application] name = Fontconfig [xdg_configuration_files] fontconfig ================================================ FILE: src/mackup/applications/fontexplorer-x.cfg ================================================ [application] name = FontExplorer X [configuration_files] Library/Application Support/Linotype/FontExplorer X FontExplorer X ================================================ FILE: src/mackup/applications/forge.cfg ================================================ [application] name = Forge [configuration_files] Library/Application Support/Forge ================================================ FILE: src/mackup/applications/fork.cfg ================================================ [application] name = Fork [configuration_files] Library/Application Support/com.DanPristupov.Fork/custom-commands.json Library/Preferences/com.DanPristupov.Fork.plist ================================================ FILE: src/mackup/applications/forklift.cfg ================================================ [application] name = ForkLift [configuration_files] Library/Preferences/com.binarynights.ForkLift2.plist Library/Preferences/com.binarynights.ForkLift-3.plist Library/Application Support/ForkLift/Favorites/Favorites.json ================================================ FILE: src/mackup/applications/franz.cfg ================================================ [application] name = Franz [configuration_files] Library/Application Support/Franz/settings [xdg_configuration_files] Franz/settings ================================================ FILE: src/mackup/applications/gasmask.cfg ================================================ [application] name = Gas Mask [configuration_files] Library/Gas Mask Library/Preferences/ee.clockwise.gmask.plist ================================================ FILE: src/mackup/applications/gdb.cfg ================================================ [application] name = gdb [configuration_files] .gdbinit ================================================ FILE: src/mackup/applications/gearplayer.cfg ================================================ [application] name = Gear Player [configuration_files] Library/Preferences/com.treasurebox.gear.plist Library/Preferences/com.treasurebox.magickeys.plist Library/Application Support/Gear Player/paddata.padl ================================================ FILE: src/mackup/applications/geektool.cfg ================================================ [application] name = GeekTool [configuration_files] Library/Preferences/org.tynsoe.GeekTool.plist Library/Preferences/org.tynsoe.geeklet.file.plist Library/Preferences/org.tynsoe.geeklet.image.plist Library/Preferences/org.tynsoe.geeklet.shell.plist Library/Preferences/org.tynsoe.geeklet.web.plist Library/Preferences/org.tynsoe.geektool3.plist ================================================ FILE: src/mackup/applications/ghci.cfg ================================================ [application] name = ghci [configuration_files] .ghci ================================================ FILE: src/mackup/applications/ghidra.cfg ================================================ [application] name = Ghidra [configuration_files] .ghidra ================================================ FILE: src/mackup/applications/ghostty.cfg ================================================ [application] name = Ghostty [xdg_configuration_files] ghostty/config ================================================ FILE: src/mackup/applications/ghostwriter.cfg ================================================ [application] name = Ghostwriter [xdg_configuration_files] ghostwriter ================================================ FILE: src/mackup/applications/gimp.cfg ================================================ [application] name = gimp [configuration_files] .gimp/colorrc .gimp/contextrc .gimp/controllerrc .gimp/devicerc .gimp/dockrc .gimp/gimprc .gimp/gtkrc .gimp/menurc .gimp/parasiterc .gimp/pluginrc .gimp/profilerc .gimp/sessionrc .gimp/templaterc .gimp/themerc .gimp/toolrc .gimp/unitrc Library/Application Support/GIMP/2.8/colorrc Library/Application Support/GIMP/2.8/contextrc Library/Application Support/GIMP/2.8/controllerrc Library/Application Support/GIMP/2.8/devicerc Library/Application Support/GIMP/2.8/dockrc Library/Application Support/GIMP/2.8/gimprc Library/Application Support/GIMP/2.8/gtkrc Library/Application Support/GIMP/2.8/menurc Library/Application Support/GIMP/2.8/parasiterc Library/Application Support/GIMP/2.8/pluginrc Library/Application Support/GIMP/2.8/profilerc Library/Application Support/GIMP/2.8/sessionrc Library/Application Support/GIMP/2.8/templaterc Library/Application Support/GIMP/2.8/themerc Library/Application Support/GIMP/2.8/toolrc Library/Application Support/GIMP/2.8/unitrc Library/Application Support/GIMP/2.10/colorrc Library/Application Support/GIMP/2.10/contextrc Library/Application Support/GIMP/2.10/controllerrc Library/Application Support/GIMP/2.10/devicerc Library/Application Support/GIMP/2.10/dockrc Library/Application Support/GIMP/2.10/gimprc Library/Application Support/GIMP/2.10/gtkrc Library/Application Support/GIMP/2.10/menurc Library/Application Support/GIMP/2.10/parasiterc Library/Application Support/GIMP/2.10/pluginrc Library/Application Support/GIMP/2.10/profilerc Library/Application Support/GIMP/2.10/sessionrc Library/Application Support/GIMP/2.10/templaterc Library/Application Support/GIMP/2.10/themerc Library/Application Support/GIMP/2.10/toolrc Library/Application Support/GIMP/2.10/unitrc ================================================ FILE: src/mackup/applications/git-hooks.cfg ================================================ [application] name = Git Hooks [configuration_files] .git_hooks ================================================ FILE: src/mackup/applications/git.cfg ================================================ [application] name = Git [configuration_files] .gitconfig [xdg_configuration_files] git/config git/ignore git/attributes ================================================ FILE: src/mackup/applications/gitbox.cfg ================================================ [application] name = Gitbox [configuration_files] Library/Preferences/com.oleganza.gitbox.plist ================================================ FILE: src/mackup/applications/gitfox.cfg ================================================ [application] name = GitFox [configuration_files] Library/Preferences/com.bytieful.Gitfox.plist Library/Preferences/com.bytieful.Gitfox-retail.plist Library/Preferences/com.bytieful.Gitfox-setapp.plist ================================================ FILE: src/mackup/applications/github-cli.cfg ================================================ [application] name = GitHub CLI [xdg_configuration_files] gh/config.yml ================================================ FILE: src/mackup/applications/gitkraken.cfg ================================================ [application] name = GitKraken [configuration_files] .gitkraken Library/Preferences/com.axosoft.gitkraken.plist ================================================ FILE: src/mackup/applications/gitup.cfg ================================================ [application] name = GitUp [configuration_files] Library/Preferences/co.gitup.mac.plist ================================================ FILE: src/mackup/applications/gmail-notifr.cfg ================================================ [application] name = Gmail Notifr [configuration_files] Library/Preferences/com.ashchan.GmailNotifr.plist ================================================ FILE: src/mackup/applications/gmailctl.cfg ================================================ [application] name = gmailctl [configuration_files] .gmailctl/config.jsonnet .gmailctl/credentials.json .gmailctl/gmailctl.libsonnet ================================================ FILE: src/mackup/applications/gmvault.cfg ================================================ [application] name = GMVault [configuration_files] .gmvault/gmvault_defaults.conf ================================================ FILE: src/mackup/applications/gnu-stow.cfg ================================================ [application] name = GNU Stow [configuration_files] .stowrc .stow-global-ignore ================================================ FILE: src/mackup/applications/gnupg.cfg ================================================ [application] name = GnuPG [configuration_files] .gnupg/gpg-agent.conf .gnupg/gpg.conf .gnupg/trustdb.gpg ================================================ FILE: src/mackup/applications/go2shell.cfg ================================================ [application] name = Go2Shell [configuration_files] Library/Preferences/com.zipzapmac.Go2Shell.plist ================================================ FILE: src/mackup/applications/goku.cfg ================================================ [application] name = Goku [xdg_configuration_files] karabiner.edn ================================================ FILE: src/mackup/applications/goland.cfg ================================================ [application] name = GoLand [configuration_files] Library/Application Support/Gogland1.0 Library/Application Support/GoLand2017.3 Library/Application Support/GoLand2018.1 Library/Application Support/GoLand2018.2 Library/Application Support/GoLand2018.3 Library/Application Support/GoLand2019.2 Library/Preferences/com.jetbrains.gogland-EAP.plist Library/Preferences/Gogland1.0 Library/Preferences/GoLand2017.3 Library/Preferences/GoLand2018.1 Library/Preferences/GoLand2018.2 Library/Preferences/GoLand2018.3 Library/Preferences/GoLand2019.2 Library/Application Support/JetBrains/GoLand2023.1 Library/Application Support/JetBrains/GoLand2023.2 ================================================ FILE: src/mackup/applications/goldendict.cfg ================================================ [application] name = Goldendict [configuration_files] .goldendict/config ================================================ FILE: src/mackup/applications/goodsync.cfg ================================================ [application] name = GoodSync [configuration_files] .goodsync ================================================ FILE: src/mackup/applications/goshare.cfg ================================================ [application] name = GoShare [xdg_configuration_files] dictget/goshare ================================================ FILE: src/mackup/applications/gradle.cfg ================================================ [application] name = Gradle [configuration_files] .gradle/gradle.properties .gradle/init.gradle .gradle/init.d ================================================ FILE: src/mackup/applications/grandtotal-3.cfg ================================================ [application] name = GrandTotal 3 [configuration_files] Library/Application Support/GrandTotal/AddressBook.plist Library/Preferences/com.mediaatelier.GrandTotal3.plist ================================================ FILE: src/mackup/applications/grsync.cfg ================================================ [application] name = grsync [configuration_files] .grsync/grsync.ini ================================================ FILE: src/mackup/applications/gstm.cfg ================================================ [application] name = Gnome SSH Tunnel Manager [configuration_files] .gSTM ================================================ FILE: src/mackup/applications/hammerspoon.cfg ================================================ [application] name = Hammerspoon [configuration_files] .hammerspoon ================================================ FILE: src/mackup/applications/handbrake.cfg ================================================ [application] name = HandBrake [configuration_files] Library/Containers/fr.handbrake.HandBrake/Data/Library/Application Support/HandBrake/UserPresets.json ================================================ FILE: src/mackup/applications/hands-off.cfg ================================================ [application] name = Hands Off! [configuration_files] Library/Preferences/com.metakine.handsoff.plist ================================================ FILE: src/mackup/applications/hazel.cfg ================================================ [application] name = Hazel [configuration_files] Library/Application Support/Hazel Library/Preferences/com.noodlesoft.Hazel.plist ================================================ FILE: src/mackup/applications/hero-lab.cfg ================================================ [application] name = Hero Lab [configuration_files] Library/Application Support/Hero Lab/customoutput Library/Application Support/Hero Lab/data Library/Application Support/Hero Lab/docs ================================================ FILE: src/mackup/applications/heroku.cfg ================================================ [application] name = Heroku [configuration_files] .heroku/accounts .heroku/plugins ================================================ FILE: src/mackup/applications/hexchat.cfg ================================================ [application] name = HexChat [xdg_configuration_files] hexchat ================================================ FILE: src/mackup/applications/hexels.cfg ================================================ [application] name = Hexels [configuration_files] Library/Preferences/com.hex-ray.hexels.plist ================================================ FILE: src/mackup/applications/hocus-focus.cfg ================================================ [application] name = Hocus Focus [configuration_files] Library/Preferences/com.uglyapps.HocusFocus.plist Library/Application Support/com.uglyapps.HocusFocus/HocusFocus.db Library/Application Support/com.uglyapps.HocusFocus/HocusFocus.db-shm Library/Application Support/com.uglyapps.HocusFocus/HocusFocus.db-wal ================================================ FILE: src/mackup/applications/homebrew.cfg ================================================ [application] name = Homebrew [configuration_files] .Brewfile Brewfile ================================================ FILE: src/mackup/applications/homebridge.cfg ================================================ [application] name = Homebridge [configuration_files] .homebridge ================================================ FILE: src/mackup/applications/houdini.cfg ================================================ [application] name = Houdini [configuration_files] Library/Preferences/com.uglyapps.Houdini.plist Library/Application Support/Houdini ================================================ FILE: src/mackup/applications/hstr.cfg ================================================ [application] name = hstr [configuration_files] .hstr_favorites .hstr_blacklist ================================================ FILE: src/mackup/applications/htop.cfg ================================================ [application] name = Htop [configuration_files] .htoprc [xdg_configuration_files] htop/htoprc ================================================ FILE: src/mackup/applications/httpie.cfg ================================================ [application] name = HTTPie [configuration_files] .httpie/config.json ================================================ FILE: src/mackup/applications/hub.cfg ================================================ [application] name = hub [xdg_configuration_files] hub ================================================ FILE: src/mackup/applications/hyper.cfg ================================================ [application] name = Hyper.app [configuration_files] .hyper.js ================================================ FILE: src/mackup/applications/hyperdock.cfg ================================================ [application] name = HyperDock [configuration_files] Library/Application Support/HyperDock Library/Preferences/de.bahoom.HyperDock.plist Library/Preferences/de.bahoom.HyperDock.prefpane.plist ================================================ FILE: src/mackup/applications/hyperswitch.cfg ================================================ [application] name = HyperSwitch [configuration_files] Library/Preferences/com.bahoom.HyperSwitch.plist ================================================ FILE: src/mackup/applications/i2cssh.cfg ================================================ [application] name = i2cssh [configuration_files] .i2csshrc ================================================ FILE: src/mackup/applications/i3.cfg ================================================ [application] name = i3 [configuration_files] .i3/config .i3status.conf [xdg_configuration_files] i3/config ================================================ FILE: src/mackup/applications/idapro.cfg ================================================ [application] name = IDA Pro [configuration_files] .idapro ================================================ FILE: src/mackup/applications/ideavim.cfg ================================================ [application] name=IdeaVim [configuration_files] .ideavimrc ================================================ FILE: src/mackup/applications/iina.cfg ================================================ [application] name = IINA [configuration_files] Library/Application Support/com.colliderli.iina/input_conf Library/Preferences/com.colliderli.iina.plist ================================================ FILE: src/mackup/applications/illustrator.cfg ================================================ [application] name = Adobe Illustrator [configuration_files] Library/Application Support/Adobe/Adobe Illustrator 17 Library/Application Support/Adobe/Adobe Illustrator 18 Library/Application Support/Adobe/Adobe Illustrator 19 Library/Application Support/Adobe/Adobe Illustrator 20 Library/Application Support/Adobe/Adobe Illustrator 23/en_US/Adobe SVG Filters.svg Library/Application Support/Adobe/Adobe Illustrator 23/en_US/Swatches Library/Application Support/Adobe/Adobe Illustrator 24/en_US/Adobe SVG Filters.svg Library/Application Support/Adobe/Adobe Illustrator 24/en_US/Swatches Library/Application Support/Adobe/OOBE Library/Preferences/Adobe Illustrator 17 Settings Library/Preferences/Adobe Illustrator 18 Settings Library/Preferences/Adobe Illustrator 19 Settings Library/Preferences/Adobe Illustrator 20 Settings Library/Preferences/Adobe Illustrator 23 Settings/en_US/Adobe Illustrator Prefs Library/Preferences/Adobe Illustrator 23 Settings/en_US/Last Used Artboard Export Settings Library/Preferences/Adobe Illustrator 23 Settings/en_US/Last Used Asset Export Settings Library/Preferences/Adobe Illustrator 23 Settings/en_US/Modified Workspaces Library/Preferences/Adobe Illustrator 23 Settings/en_US/Perspective grid Presets Library/Preferences/Adobe Illustrator 23 Settings/en_US/Print Presets Library/Preferences/Adobe Illustrator 23 Settings/en_US/Tools/Tools Panel Presets Library/Preferences/Adobe Illustrator 23 Settings/en_US/Transparency flattener presets Library/Preferences/Adobe Illustrator 23 Settings/en_US/VariableWidthProfiles Library/Preferences/Adobe Illustrator 23 Settings/en_US/Vectorizing Presets Library/Preferences/Adobe Illustrator 23 Settings/en_US/Workspaces Library/Preferences/Adobe Illustrator 24 Settings/en_US/Adobe Illustrator Prefs Library/Preferences/Adobe Illustrator 24 Settings/en_US/Last Used Artboard Export Settings Library/Preferences/Adobe Illustrator 24 Settings/en_US/Last Used Asset Export Settings Library/Preferences/Adobe Illustrator 24 Settings/en_US/Modified Workspaces Library/Preferences/Adobe Illustrator 24 Settings/en_US/Perspective grid Presets Library/Preferences/Adobe Illustrator 24 Settings/en_US/Print Presets Library/Preferences/Adobe Illustrator 24 Settings/en_US/Tools/Tools Panel Presets Library/Preferences/Adobe Illustrator 24 Settings/en_US/Transparency flattener presets Library/Preferences/Adobe Illustrator 24 Settings/en_US/VariableWidthProfiles Library/Preferences/Adobe Illustrator 24 Settings/en_US/Vectorizing Presets Library/Preferences/Adobe Illustrator 24 Settings/en_US/Workspaces Library/Preferences/com.adobe.illustrator.plist ================================================ FILE: src/mackup/applications/inkscape.cfg ================================================ [application] name = inkscape [xdg_configuration_files] inkscape/preferences.xml ================================================ FILE: src/mackup/applications/insomnia.cfg ================================================ [application] name = Insomnia [configuration_files] Library/Application Support/Insomnia [xdg_configuration_files] Insomnia ================================================ FILE: src/mackup/applications/intellijidea.cfg ================================================ [application] name = IntelliJ IDEA [configuration_files] .IntelliJIdea12/config .IntelliJIdea13/config .IntelliJIdea14/config .IntelliJIdea15/config IdeaIC2018.2/config IdeaIC2018.3/config IdeaIC2019.1/config IdeaIC2019.2/config IdeaIC2019.3/config IntelliJIdea2016.1/config IntelliJIdea2016.2/config IntelliJIdea2016.3/config IntelliJIdea2017.1/config IntelliJIdea2017.2/config IntelliJIdea2017.3/config IntelliJIdea2018.1/config IntelliJIdea2018.2/config IntelliJIdea2018.3/config IntelliJIdea2019.1/config IntelliJIdea2019.2/config IntelliJIdea2019.3/config Library/Application Support/IdeaIC2016.1 Library/Application Support/IdeaIC2017.1 Library/Application Support/IdeaIC2017.2 Library/Application Support/IdeaIC2017.3 Library/Application Support/IdeaIC2018.2 Library/Application Support/IdeaIC2018.3 Library/Application Support/IdeaIC2019.1 Library/Application Support/IntelliJIdea12 Library/Application Support/IntelliJIdea13 Library/Application Support/IntelliJIdea14 Library/Application Support/IntelliJIdea15 Library/Application Support/IntelliJIdea2016.1 Library/Application Support/IntelliJIdea2016.2 Library/Application Support/IntelliJIdea2016.3 Library/Application Support/IntelliJIdea2017.1 Library/Application Support/IntelliJIdea2017.2 Library/Application Support/IntelliJIdea2017.3 Library/Application Support/IntelliJIdea2018.1 Library/Application Support/IntelliJIdea2018.2 Library/Application Support/IntelliJIdea2018.3 Library/Application Support/IntelliJIdea2019.1 Library/Application Support/IntelliJIdea2019.2 Library/Application Support/IntelliJIdea2019.3 Library/Application Support/JetBrains/IntelliJIdea2020.1 Library/Application Support/JetBrains/IntelliJIdea2020.2 Library/Application Support/JetBrains/IntelliJIdea2020.3 Library/Application Support/JetBrains/IntelliJIdea2021.1 Library/Application Support/JetBrains/IntelliJIdea2022.1 Library/Application Support/JetBrains/IntelliJIdea2022.2 Library/Application Support/JetBrains/IntelliJIdea2023.1 Library/Application Support/JetBrains/IntelliJIdea2023.2 Library/Preferences/IdeaIC2016.1 Library/Preferences/IdeaIC2016.2 Library/Preferences/IdeaIC2016.3 Library/Preferences/IdeaIC2017.1 Library/Preferences/IdeaIC2017.2 Library/Preferences/IdeaIC2017.3 Library/Preferences/IdeaIC2018.2 Library/Preferences/IdeaIC2018.3 Library/Preferences/IdeaIC2019.1 Library/Preferences/IntelliJIdea12 Library/Preferences/IntelliJIdea13 Library/Preferences/IntelliJIdea14 Library/Preferences/IntelliJIdea15 Library/Preferences/IntelliJIdea2016.1 Library/Preferences/IntelliJIdea2016.2 Library/Preferences/IntelliJIdea2016.3 Library/Preferences/IntelliJIdea2017.1 Library/Preferences/IntelliJIdea2017.2 Library/Preferences/IntelliJIdea2017.3 Library/Preferences/IntelliJIdea2018.1 Library/Preferences/IntelliJIdea2018.2 Library/Preferences/IntelliJIdea2018.3 Library/Preferences/IntelliJIdea2019.1 Library/Preferences/IntelliJIdea2019.2 Library/Preferences/IntelliJIdea2019.3 ================================================ FILE: src/mackup/applications/ipython.cfg ================================================ [application] name = IPython [configuration_files] .ipython ================================================ FILE: src/mackup/applications/irssi.cfg ================================================ [application] name = Irssi [configuration_files] .irssi ================================================ FILE: src/mackup/applications/istat-menus.cfg ================================================ [application] name = iStat Menus [configuration_files] Library/Preferences/com.bjango.istatmenus.menubar.7.plist Library/Preferences/com.bjango.istatmenus.plist Library/Preferences/com.bjango.istatmenus.status.plist Library/Preferences/com.bjango.istatmenus5.extras.plist Library/Preferences/com.bjango.istatmenus6.extras.plist ================================================ FILE: src/mackup/applications/iterm2.cfg ================================================ [application] name = iTerm2 [configuration_files] Library/Preferences/com.googlecode.iterm2.plist [xdg_configuration_files] iterm2/AppSupport/DynamicProfiles ================================================ FILE: src/mackup/applications/itermocil.cfg ================================================ [application] name = iTermocil [configuration_files] .itermocil ================================================ FILE: src/mackup/applications/itsycal.cfg ================================================ [application] name = Itsycal [configuration_files] Library/Preferences/com.mowglii.ItsycalApp.plist ================================================ FILE: src/mackup/applications/itunes-scripts.cfg ================================================ [application] name = iTunes Scripts [configuration_files] Library/iTunes/Scripts ================================================ FILE: src/mackup/applications/jankyborders.cfg ================================================ [application] name = Janky Borders [xdg_configuration_files] borders/bordersrc ================================================ FILE: src/mackup/applications/janus.cfg ================================================ [application] name = Janus [configuration_files] .janus .vimrc.before .vimrc.after ================================================ FILE: src/mackup/applications/jitouch.cfg ================================================ [application] name = Jitouch [configuration_files] Library/Preferences/com.jitouch.Jitouch.plist ================================================ FILE: src/mackup/applications/joplin.cfg ================================================ [application] name = Joplin [xdg_configuration_files] joplin-desktop/userchrome.css joplin-desktop/userstyle.css ================================================ FILE: src/mackup/applications/jrnl.cfg ================================================ [application] name = jrnl [configuration_files] .jrnl_config [xdg_configuration_files] jrnl/jrnl.yaml ================================================ FILE: src/mackup/applications/jsbeautifier.cfg ================================================ [application] name = JS Beautifier [configuration_files] .jsbeautifyrc ================================================ FILE: src/mackup/applications/jshint.cfg ================================================ [application] name = JSHint [configuration_files] .jshintrc ================================================ FILE: src/mackup/applications/julia.cfg ================================================ [application] name = Julia [configuration_files] .juliarc.jl .julia/config/startup.jl ================================================ FILE: src/mackup/applications/jumpcut.cfg ================================================ [application] name = Jumpcut [configuration_files] Library/Application Support/Jumpcut Library/Preferences/net.sf.Jumpcut.plist ================================================ FILE: src/mackup/applications/jupyter.cfg ================================================ [application] name = Jupyter [configuration_files] .jupyter ================================================ FILE: src/mackup/applications/k9s.cfg ================================================ [application] name = k9s [xdg_configuration_files] k9s/config.yml k9s/skin.yml ================================================ FILE: src/mackup/applications/kaggle.cfg ================================================ [application] name = Kaggle [configuration_files] .kaggle/kaggle.json ================================================ FILE: src/mackup/applications/kaleidoscope.cfg ================================================ [application] name = Kaleidoscope [configuration_files] Library/Preferences/com.blackpixel.kaleidoscope.plist Library/Application Support/Kaleidoscope ================================================ FILE: src/mackup/applications/karabiner-elements.cfg ================================================ [application] name = Karabiner Elements [xdg_configuration_files] karabiner ================================================ FILE: src/mackup/applications/karabiner.cfg ================================================ [application] name = Karabiner [configuration_files] Library/Preferences/org.pqrs.Karabiner.plist Library/Preferences/org.pqrs.Karabiner-AXNotifier.plist Library/Preferences/org.pqrs.Karabiner.multitouchextension.plist Library/Application Support/Karabiner ================================================ FILE: src/mackup/applications/kdenlive.cfg ================================================ [application] name = Kdenlive [configuration_files] kdenlive [xdg_configuration_files] kdenliverc ================================================ FILE: src/mackup/applications/keepassx.cfg ================================================ [application] name = KeePassX [configuration_files] .keepassx Library/Preferences/org.keepassx.keepassx.plist Library/Application Support/keepassx/keepassx2.ini [xdg_configuration_files] keepassx/keepassx2.ini ================================================ FILE: src/mackup/applications/keepassxc.cfg ================================================ [application] name = KeePassXC [configuration_files] Library/Preferences/org.keepassxc.keepassxc.plist Library/Application Support/keepassxc/keepassxc.ini ================================================ FILE: src/mackup/applications/keepingyouawake.cfg ================================================ [application] name = KeepingYouAwake [configuration_files] Library/Preferences/info.marcel-dierkes.KeepingYouAwake.plist ================================================ FILE: src/mackup/applications/keka.cfg ================================================ [application] name = Keka [configuration_files] Library/Preferences/com.aone.keka.plist ================================================ FILE: src/mackup/applications/keybase.cfg ================================================ [application] name = Keybase [configuration_files] .keybase/config.json ================================================ FILE: src/mackup/applications/keyboard-maestro.cfg ================================================ [application] name = Keyboard Maestro [configuration_files] Library/Application Support/Keyboard Maestro ================================================ FILE: src/mackup/applications/keymo.cfg ================================================ [application] name = Keymo [configuration_files] Library/Preferences/com.manytricks.Keymo.plist ================================================ FILE: src/mackup/applications/keyremap4macbook.cfg ================================================ [application] name = KeyRemap4MacBook [configuration_files] Library/Preferences/org.pqrs.KeyRemap4MacBook.plist Library/Preferences/org.pqrs.KeyRemap4MacBook.multitouchextension.plist Library/Application Support/KeyRemap4MacBook/private.xml ================================================ FILE: src/mackup/applications/khd.cfg ================================================ [application] name = khd [configuration_files] .khdrc ================================================ FILE: src/mackup/applications/kiro.cfg ================================================ [application] name = Kiro [configuration_files] Library/Application Support/Kiro/User/snippets Library/Application Support/Kiro/User/keybindings.json Library/Application Support/Kiro/User/settings.json ================================================ FILE: src/mackup/applications/kitty.cfg ================================================ [application] name = kitty [xdg_configuration_files] kitty/kitty.conf ================================================ FILE: src/mackup/applications/krew.cfg ================================================ [application] name = Krew [configuration_files] .krew ================================================ FILE: src/mackup/applications/kubectl.cfg ================================================ [application] name = kubectl [configuration_files] .kube/config ================================================ FILE: src/mackup/applications/kwm.cfg ================================================ [application] name = Kwm [configuration_files] .kwm ================================================ FILE: src/mackup/applications/latexit.cfg ================================================ [application] name = LaTeXiT [configuration_files] Library/Preferences/fr.chachatelier.pierre.LaTeXiT.plist ================================================ FILE: src/mackup/applications/launchbar.cfg ================================================ [application] name = LaunchBar [configuration_files] Library/Preferences/at.obdev.LaunchBar.plist Library/Application Support/LaunchBar/Actions Library/Application Support/LaunchBar/Snippets Library/Application Support/LaunchBar/Configuration.plist Library/Application Support/LaunchBar/CustomShortcuts.plist ================================================ FILE: src/mackup/applications/lazydocker.cfg ================================================ [application] name = lazydocker [configuration_files] Library/Application Support/jesseduffield/lazydocker/config.yml ================================================ FILE: src/mackup/applications/lazygit.cfg ================================================ [application] name = lazygit [configuration_files] Library/Application Support/jesseduffield/lazygit/config.yml Library/Application Support/lazygit/config.yml [xdg_configuration_files] lazygit/config.yml ================================================ FILE: src/mackup/applications/ledger.cfg ================================================ [application] name = ledger [configuration_files] .ledgerrc ================================================ FILE: src/mackup/applications/leiningen.cfg ================================================ [application] name = Leiningen [configuration_files] .lein ================================================ FILE: src/mackup/applications/lf.cfg ================================================ [application] name = lf [xdg_configuration_files] lf/lfrc ================================================ FILE: src/mackup/applications/libreoffice.cfg ================================================ [application] name = LibreOffice [xdg_configuration_files] libreoffice ================================================ FILE: src/mackup/applications/liftoff.cfg ================================================ [application] name = liftoff [configuration_files] .liftoffrc ================================================ FILE: src/mackup/applications/light-table.cfg ================================================ [application] name = Light Table [configuration_files] Library/Application Support/LightTable/plugins Library/Application Support/LightTable/settings Library/Application Support/LightTable/User Library/Preferences/com.kodowa.LightTable.plist ================================================ FILE: src/mackup/applications/lightpaper.cfg ================================================ [application] name = LightPaper [configuration_files] .lightpaper Library/Application Support/LightPaper/customCSS.css Library/Preferences/com.42squares.LightPaper.plist ================================================ FILE: src/mackup/applications/lightroom-classic.cfg ================================================ [application] name = Adobe Lightroom Classic [configuration_files] Library/Preferences/com.adobe.LightroomClassicCC7.plist ================================================ FILE: src/mackup/applications/lightroom.cfg ================================================ [application] name = Adobe Lightroom [configuration_files] Library/Application Support/Adobe/Lightroom Library/Preferences/com.adobe.Lightroom4.plist Library/Preferences/com.adobe.Lightroom5.plist Library/Preferences/com.adobe.Lightroom6.plist ================================================ FILE: src/mackup/applications/limechat.cfg ================================================ [application] name = LimeChat [configuration_files] Library/Preferences/net.limechat.LimeChat-AppStore.plist ================================================ FILE: src/mackup/applications/liquidprompt.cfg ================================================ [application] name = Liquid Prompt [configuration_files] .liquidpromptrc [xdg_configuration_files] liquidpromptrc ================================================ FILE: src/mackup/applications/littlesnitch.cfg ================================================ [application] name = LittleSnitch [configuration_files] Library/Preferences/at.obdev.LittleSnitchNetworkMonitor.plist Library/Preferences/at.obdev.littlesnitch.networkmonitor.plist Library/Application Support/Little Snitch/rules.usr.xpl Library/Application Support/Little Snitch/configuration.xpl Library/Application Support/Little Snitch/configuration.user.xpl Library/Application Support/Little Snitch/configuration4.user.xpl ================================================ FILE: src/mackup/applications/livestreamer.cfg ================================================ [application] name = Livestreamer [configuration_files] .livestreamerrc [xdg_configuration_files] livestreamer/config livestreamer/plugins ================================================ FILE: src/mackup/applications/logitech-options.cfg ================================================ [application] name = Logitech Options [configuration_files] Library/Preferences/com.logitech.manager.setting.ffff.plist ================================================ FILE: src/mackup/applications/logseq.cfg ================================================ [application] name = Logseq [configuration_files] .logseq ================================================ FILE: src/mackup/applications/lollypop.cfg ================================================ [application] name = Lollypop [configuration_files] .local/share/lollypop ================================================ FILE: src/mackup/applications/loopback.cfg ================================================ [application] name = Loopback [configuration_files] Library/Preferences/com.rogueamoeba.Loopback.plist Library/Application Support/Loopback/Devices.plist ================================================ FILE: src/mackup/applications/luftrausers.cfg ================================================ [application] name = Luftrausers [configuration_files] .LUFTRAUSERS ================================================ FILE: src/mackup/applications/lunarvim.cfg ================================================ [application] name = LunarVim [xdg_configuration_files] lvim/config.lua ================================================ FILE: src/mackup/applications/macdive2.cfg ================================================ [application] name = MacDive [configuration_files] Library/Application Support/MacDive Library/Preferences/com.mintsoftware.MacDive2.plist ================================================ FILE: src/mackup/applications/macdown.cfg ================================================ [application] name = MacDown [configuration_files] Library/Preferences/com.uranusjr.macdown.plist Library/Application Support/MacDown/Styles Library/Application Support/MacDown/Themes ================================================ FILE: src/mackup/applications/mackup.cfg ================================================ [application] name = Mackup [configuration_files] .mackup.cfg .mackup ================================================ FILE: src/mackup/applications/macosx.cfg ================================================ [application] name = MacOSX [configuration_files] .MacOSX Library/KeyBindings/DefaultKeyBinding.dict Library/PDF Services Library/Preferences/com.apple.symbolichotkeys.plist Library/Preferences/.GlobalPreferences.plist Library/Scripts Library/Speech/Speakable Items Library/Workflows ================================================ FILE: src/mackup/applications/macvim.cfg ================================================ [application] name = MacVim [configuration_files] Library/Preferences/org.vim.MacVim.LSSharedFileList.plist Library/Preferences/org.vim.MacVim.plist ================================================ FILE: src/mackup/applications/magic-launch.cfg ================================================ [application] name = Magic Launch [configuration_files] Library/Preferences/com.metakine.magic-launch.agent.plist ================================================ FILE: src/mackup/applications/magicprefs.cfg ================================================ [application] name = MagicPrefs [configuration_files] Library/Preferences/com.vladalexa.MagicPrefs.MagicPrefsPlugins.plist Library/Preferences/com.vladalexa.MagicPrefs.plist ================================================ FILE: src/mackup/applications/magnet.cfg ================================================ [application] name = Magnet [configuration_files] Library/Preferences/com.crowdcafe.windowmagnet.plist ================================================ FILE: src/mackup/applications/maid.cfg ================================================ [application] name = Maid [configuration_files] .maid ================================================ FILE: src/mackup/applications/mail.cfg ================================================ [application] name = Mail [configuration_files] Library/Preferences/com.apple.mail.plist ================================================ FILE: src/mackup/applications/mailmate.cfg ================================================ [application] name = Mailmate [configuration_files] Library/Preferences/com.freron.MailMate.plist Library/Application Support/Mailmate/com.freron.MailMate.pid Library/Application Support/Mailmate/Identities.plist Library/Application Support/Mailmate/Mailboxes.plist Library/Application Support/Mailmate/Signatures.plist Library/Application Support/Mailmate/Sources.plist Library/Application Support/Mailmate/Submission.plist Library/Application Support/Mailmate/Tags.plist Library/Application Support/Mailmate/Resources/KeyBindings ================================================ FILE: src/mackup/applications/mailplane.cfg ================================================ [application] name = Mailplane [configuration_files] Library/Preferences/com.mailplaneapp.Mailplane.plist Library/Preferences/com.mailplaneapp.Mailplane3.plist ================================================ FILE: src/mackup/applications/mako.cfg ================================================ [application] name = mako [xdg_configuration_files] mako/config ================================================ FILE: src/mackup/applications/marked2.cfg ================================================ [application] name = Marked 2 [configuration_files] Library/Application Support/Marked 2 Library/Preferences/com.brettterpstra.marked2.plist ================================================ FILE: src/mackup/applications/marta.cfg ================================================ [application] name = Marta [configuration_files] Library/Application Support/org.yanex.marta/conf.json Library/Application Support/org.yanex.marta/favorites.json Library/Application Support/org.yanex.marta/conf.marco Library/Application Support/org.yanex.marta/favorites.marco ================================================ FILE: src/mackup/applications/matlab.cfg ================================================ [application] name = MATLAB [configuration_files] .matlab ================================================ FILE: src/mackup/applications/maven.cfg ================================================ [application] name = Maven [configuration_files] .m2/settings-security.xml .m2/settings.xml .m2/toolchains.xml ================================================ FILE: src/mackup/applications/max.cfg ================================================ [application] name = Max [configuration_files] Library/Preferences/org.sbooth.Max.plist ================================================ FILE: src/mackup/applications/mendeley.cfg ================================================ [application] name = Mendelay Desktop [configuration_files] Library/Application Support/Mendeley Desktop ================================================ FILE: src/mackup/applications/menumeters.cfg ================================================ [application] name = MenuMeters [configuration_files] Library/Preferences/com.ragingmenace.MenuMeters.plist ================================================ FILE: src/mackup/applications/mercurial.cfg ================================================ [application] name = Mercurial [configuration_files] .hgrc .hgignore_global ================================================ FILE: src/mackup/applications/mercurymover.cfg ================================================ [application] name = MercuryMover [configuration_files] Library/Preferences/com.heliumfoot.MyWiAgent.plist ================================================ FILE: src/mackup/applications/messages.cfg ================================================ [application] name = Messages [configuration_files] Library/Preferences/com.apple.iChat.AIM.plist Library/Preferences/com.apple.iChat.Jabber.plist Library/Preferences/com.apple.iChat.StatusMessages.plist Library/Preferences/com.apple.iChat.Yahoo.plist ================================================ FILE: src/mackup/applications/micro.cfg ================================================ [application] name = Micro [xdg_configuration_files] micro/colorschemes micro/syntax micro/settings.json micro/bindings.json ================================================ FILE: src/mackup/applications/microsoft-remote-desktop.cfg ================================================ [application] name = Microsoft Remote Desktop [configuration_files] Library/Containers/com.microsoft.rdc.macos/Data/Library/Preferences/com.microsoft.rdc.macos.plist ================================================ FILE: src/mackup/applications/mise.cfg ================================================ [application] name = mise-en-place [configuration_files] mise.toml mise/config.toml .mise.toml .mise/config.toml [xdg_configuration_files] mise.toml mise/config.toml mise/conf.d .mise.toml .mise/config.toml .mise/conf.d ================================================ FILE: src/mackup/applications/mitmproxy.cfg ================================================ [application] name = mitmproxy [configuration_files] .mitmproxy/config.yaml ================================================ FILE: src/mackup/applications/mkcert.cfg ================================================ [application] name = mkcert [configuration_files] Library/Application Support/mkcert ================================================ FILE: src/mackup/applications/mole.cfg ================================================ [application] name = Mole [xdg_configuration_files] mole/whitelist ================================================ FILE: src/mackup/applications/monodevelop.cfg ================================================ [application] name = MonoDevelop [configuration_files] Library/MonoDevelop-Unity-5.0/KeyBindings/Custom.mac-kb.xml Library/Preferences/MonoDevelop-Unity-5.0/MonoDevelopProperties.xml Library/MonoDevelop-Unity-5.0/Policies/Default.mdpolicy.xml ================================================ FILE: src/mackup/applications/moom.cfg ================================================ [application] name = Moom [configuration_files] Library/Preferences/com.manytricks.Moom.plist Library/Application Support/Many Tricks ================================================ FILE: src/mackup/applications/mosaic.cfg ================================================ [application] name = Mosaic [configuration_files] Library/Application Support/com.lightpillar.Mosaic/MosaicCoreData.storedata ================================================ FILE: src/mackup/applications/mou.cfg ================================================ [application] name = Mou [configuration_files] Library/Preferences/com.mouapp.Mou.plist Library/Preferences/com.mouapp.Mou.LSSharedFileList.plist Library/Application Support/Mou ================================================ FILE: src/mackup/applications/mpd.cfg ================================================ [application] name = mpd [configuration_files] .mpd .mpdconf ================================================ FILE: src/mackup/applications/mplayerx.cfg ================================================ [application] name = MPlayerX [configuration_files] Library/Preferences/org.niltsh.MPlayerX.plist ================================================ FILE: src/mackup/applications/mps-youtube.cfg ================================================ [application] name = MPS Youtube [xdg_configuration_files] mps-youtube/config mps-youtube/playlist_v2 ================================================ FILE: src/mackup/applications/mpv.cfg ================================================ [application] name = MPV [configuration_files] .mpv/channels.conf .mpv/config .mpv/input.conf [xdg_configuration_files] mpv/config mpv/mpv.conf mpv/scripts mpv/script-opts mpv/input.conf mpv/watch_later ================================================ FILE: src/mackup/applications/mtmr.cfg ================================================ [application] name = mtmr [configuration_files] Library/Application Support/MTMR/items.json ================================================ FILE: src/mackup/applications/multitouch.cfg ================================================ [application] name = Multitouch [configuration_files] Library/Preferences/com.brassmonkery.Multitouch.plist ================================================ FILE: src/mackup/applications/mumu.cfg ================================================ [application] name = Mumu [configuration_files] Library/Application Support/Mumu ================================================ FILE: src/mackup/applications/musicbrainz-picard.cfg ================================================ [application] name = MusicBrainz Picard [xdg_configuration_files] MusicBrainz ================================================ FILE: src/mackup/applications/mutespotifyads.cfg ================================================ [application] name = MuteSpotifyAds [configuration_files] Library/Preferences/de.simonmeusel.MuteSpotifyAds.plist ================================================ FILE: src/mackup/applications/mycli.cfg ================================================ [application] name = mycli [configuration_files] .myclirc ================================================ FILE: src/mackup/applications/myrepos.cfg ================================================ [application] name = myrepos [configuration_files] .mrconfig ================================================ FILE: src/mackup/applications/mysql.cfg ================================================ [application] name = MySQL [configuration_files] .my.cnf ================================================ FILE: src/mackup/applications/mysqlworkbench.cfg ================================================ [application] name = MySQL Workbench [configuration_files] Library/Application Support/MySQL/Workbench/connections.xml Library/Application Support/MySQL/Workbench/server_instances.xml Library/Application Support/MySQL/Workbench/wb_options.xml Library/Preferences/com.oracle.mysql.workbench.plist ================================================ FILE: src/mackup/applications/name-mangler.cfg ================================================ [application] name = Name Mangler [configuration_files] Library/Application Support/Name Mangler Library/Preferences/com.manytricks.NameMangler.plist ================================================ FILE: src/mackup/applications/nano.cfg ================================================ [application] name = Nano [configuration_files] .nanorc ================================================ FILE: src/mackup/applications/navicat.cfg ================================================ [application] name = Navicat [configuration_files] Library/Application Support/PremiumSoft CyberTech Library/Application Support/Navicat for MySQL Library/Application Support/Navicat Premium Library/Preferences/com.prect.Navicat.plist Library/Preferences/com.prect.NavicatPremium.plist Library/Preferences/com.prect.NavicatPremiumEssentials.plist ================================================ FILE: src/mackup/applications/ncmpcpp.cfg ================================================ [application] name = ncmpcpp [configuration_files] .ncmpcpp ================================================ FILE: src/mackup/applications/neofetch.cfg ================================================ [application] name = Neofetch [xdg_configuration_files] neofetch/config.conf ================================================ FILE: src/mackup/applications/neovim.cfg ================================================ [application] name = neovim [configuration_files] .nvimrc .nvim [xdg_configuration_files] nvim/init.vim nvim/init.lua nvim/lua nvim/colors nvim/compiler nvim/ftplugin nvim/ftdetect nvim/indent nvim/plugin nvim/syntax ================================================ FILE: src/mackup/applications/nethack.cfg ================================================ [application] name = Nethack [configuration_files] .nethackrc ================================================ FILE: src/mackup/applications/netlify.cfg ================================================ [application] name = Netlify [configuration_files] .netlify/config.json ================================================ FILE: src/mackup/applications/newsbeuter.cfg ================================================ [application] name = newsbeuter [configuration_files] .newsbeuter/config .newsbeuter/urls ================================================ FILE: src/mackup/applications/ngrok.cfg ================================================ [application] name = ngrok [configuration_files] .ngrok .ngrok2 ================================================ FILE: src/mackup/applications/ni.cfg ================================================ [application] name = ni [configuration_files] .nirc ================================================ FILE: src/mackup/applications/nomacs.cfg ================================================ [application] name = Nomacs [xdg_configuration_files] nomacs ================================================ FILE: src/mackup/applications/nosqlbooster-for-mongodb.cfg ================================================ [application] name = NoSQLBooster for MongoDB [configuration_files] Documents/NoSQLBooster ================================================ FILE: src/mackup/applications/notion-enhancer.cfg ================================================ [application] name = notion-enhancer [configuration_files] .notion-enhancer ================================================ FILE: src/mackup/applications/nova.cfg ================================================ [application] name = Nova [configuration_files] Library/Preferences/com.panic.Nova.plist ================================================ FILE: src/mackup/applications/npm.cfg ================================================ [application] name = npm [configuration_files] .npmrc ================================================ FILE: src/mackup/applications/npmrc.cfg ================================================ [application] name = npmrc [configuration_files] .npmrcs ================================================ FILE: src/mackup/applications/nslogger.cfg ================================================ [application] name = NSLogger [configuration_files] Library/Preferences/com.florentpillet.NSLogger.plist ================================================ FILE: src/mackup/applications/nuget.cfg ================================================ [application] name = nuget [configuration_files] .nuget/NuGet/NuGet.Config ================================================ FILE: src/mackup/applications/nushell.cfg ================================================ [application] name = Nushell [configuration_files] Library/Application Support/nushell/env.nu Library/Application Support/nushell/config.nu [xdg_configuration_files] nushell/config.nu nushell/env.nu ================================================ FILE: src/mackup/applications/nvalt.cfg ================================================ [application] name = nvALT [configuration_files] Library/Preferences/net.elasticthreads.nv.plist Library/Application Support/Notational Velocity ================================================ FILE: src/mackup/applications/nvm.cfg ================================================ [application] name = nvm [configuration_files] .nvm/default-packages ================================================ FILE: src/mackup/applications/nvpy.cfg ================================================ [application] name = nvpy [configuration_files] .nvpy.cfg ================================================ FILE: src/mackup/applications/obs.cfg ================================================ [application] name = OBS [configuration_files] Library/Preferences/com.obsproject.obs-studio.plist Library/Application Support/obs-studio/global.ini Library/Application Support/obs-studio/basic ================================================ FILE: src/mackup/applications/oci.cfg ================================================ [application] name = Oracle Cloud Infrastructure CLI [configuration_files] .oci/config .oci/oci_cli_rc ================================================ FILE: src/mackup/applications/offlineimap.cfg ================================================ [application] name = OfflineIMAP [configuration_files] .offlineimaprc ================================================ FILE: src/mackup/applications/ogdesign-eagle.cfg ================================================ [application] name = Eagle (ogdesign) [configuration_files] Library/Application Support/Eagle/Settings ================================================ FILE: src/mackup/applications/oh-my-fish.cfg ================================================ [application] name = Oh My Fish [xdg_configuration_files] omf ================================================ FILE: src/mackup/applications/oh-my-tmux.cfg ================================================ [application] name = Oh My Tmux [configuration_files] .tmux.conf.local ================================================ FILE: src/mackup/applications/omnifocus.cfg ================================================ [application] name = OmniFocus [configuration_files] Library/Application Support/OmniFocus/Plug-Ins Library/Application Support/OmniFocus/Themes Library/Preferences/com.omnigroup.OmniFocus.plist ================================================ FILE: src/mackup/applications/omnigraffle.cfg ================================================ [application] name = OmniGraffle [configuration_files] Library/Application Support/The Omni Group/OmniGraffle ================================================ FILE: src/mackup/applications/openbox.cfg ================================================ [application] name = openbox [xdg_configuration_files] openbox/menu.xml openbox/rc.xml openbox/environment openbox/autostart ================================================ FILE: src/mackup/applications/opencode.cfg ================================================ [application] name = OpenCode [configuration_files] .local/share/opencode/auth.json [xdg_configuration_files] opencode/agent opencode/command opencode/themes opencode/tool opencode/settings opencode/opencode.json opencode/opencode.jsonc opencode/config.json opencode/AGENTS.md ================================================ FILE: src/mackup/applications/openemu.cfg ================================================ [application] name = OpenEmu [configuration_files] Library/Preferences/org.openemu.OpenEmu.plist Library/Application Support/OpenEmu ================================================ FILE: src/mackup/applications/opera.cfg ================================================ [application] name = Opera [configuration_files] Library/Application Support/com.operasoftware.Opera Library/Application Support/com.operasoftware.OperaDeveloper Library/Application Support/com.operasoftware.OperaNext Library/Preferences/com.operasoftware.Opera.plist Library/Preferences/com.operasoftware.OperaDeveloper.plist Library/Preferences/com.operasoftware.OperaNext.plist ================================================ FILE: src/mackup/applications/p10k.cfg ================================================ [application] name = Powerlevel10k [configuration_files] .p10k.zsh ================================================ FILE: src/mackup/applications/paintbrush.cfg ================================================ [application] name = Paintbrush [configuration_files] Library/Preferences/com.soggywaffles.Paintbrush.plist ================================================ FILE: src/mackup/applications/pandoc.cfg ================================================ [application] name = Pandoc [configuration_files] .pandoc ================================================ FILE: src/mackup/applications/pass.cfg ================================================ [application] name = pass [configuration_files] .password-store ================================================ FILE: src/mackup/applications/pastebot.cfg ================================================ [application] name = Pastebot [configuration_files] Library/Preferences/com.tapbots.PastebotSync.plist Library/Preferences/com.tapbots.PastebotSync.prefPane.plist Library/Preferences/com.tapbots.PastebotSync.stats.plist ================================================ FILE: src/mackup/applications/path-finder.cfg ================================================ [application] name = Path Finder [configuration_files] Library/Preferences/com.cocoatech.PathFinder.plist Library/Application Support/Path Finder ================================================ FILE: src/mackup/applications/pdfjam.cfg ================================================ [application] name = PDFjam [configuration_files] .pdfjam.conf ================================================ FILE: src/mackup/applications/pear.cfg ================================================ [application] name = Pear [configuration_files] .pearrc ================================================ FILE: src/mackup/applications/pentadactyl.cfg ================================================ [application] name = Pentadactyl [configuration_files] .pentadactyl .pentadactylrc ================================================ FILE: src/mackup/applications/perl.cfg ================================================ [application] name = Perl [configuration_files] .perltidyrc .perlcriticrc .proverc ================================================ FILE: src/mackup/applications/pgsql.cfg ================================================ [application] name = PostgreSQL [configuration_files] .pgpass .psqlrc ================================================ FILE: src/mackup/applications/phoenix.cfg ================================================ [application] name = Phoenix [configuration_files] .phoenix.js ================================================ FILE: src/mackup/applications/phoneview.cfg ================================================ [application] name = PhoneView [configuration_files] Library/Preferences/com.ecamm.PhoneView.plist ================================================ FILE: src/mackup/applications/photoshop.cfg ================================================ [application] name = Adobe Photoshop [configuration_files] Library/Application Support/Adobe/Adobe Photoshop CC 2013/Presets Library/Application Support/Adobe/Adobe Photoshop CC 2014/Presets Library/Application Support/Adobe/Adobe Photoshop CC 2015/Presets Library/Application Support/Adobe/Adobe Photoshop CC 2015.5/Presets Library/Application Support/Adobe/Adobe Photoshop CC 2019/Presets Library/Preferences/Adobe Photoshop CC 2013 Settings Library/Preferences/Adobe Photoshop CC 2014 Settings Library/Preferences/Adobe Photoshop CC 2015 Settings Library/Preferences/Adobe Photoshop CC 2015.5 Settings Library/Preferences/Adobe Photoshop CC 2019 Settings Library/Preferences/com.adobe.Photoshop.plist ================================================ FILE: src/mackup/applications/phpstorm.cfg ================================================ [application] name = PhpStorm [configuration_files] Library/Application Support/JetBrains/PhpStorm2020.1 Library/Application Support/JetBrains/PhpStorm2020.2 Library/Application Support/JetBrains/PhpStorm2020.3 Library/Application Support/JetBrains/PhpStorm2021.1 Library/Application Support/JetBrains/PhpStorm2021.2 Library/Application Support/JetBrains/PhpStorm2021.3 Library/Application Support/JetBrains/PhpStorm2022.1 Library/Application Support/JetBrains/PhpStorm2022.2 Library/Application Support/JetBrains/PhpStorm2022.3 Library/Application Support/JetBrains/PhpStorm2023.1 Library/Application Support/JetBrains/PhpStorm2023.2 Library/Application Support/JetBrains/PhpStorm2023.3 Library/Application Support/JetBrains/PhpStorm2024.1 Library/Application Support/PhpStorm2016.1 Library/Application Support/PhpStorm2016.2 Library/Application Support/PhpStorm2016.3 Library/Application Support/PhpStorm2017.1 Library/Application Support/PhpStorm2017.2 Library/Application Support/PhpStorm2017.3 Library/Application Support/PhpStorm2018.1 Library/Application Support/PhpStorm2018.2 Library/Application Support/PhpStorm2018.3 Library/Application Support/PhpStorm2019.1 Library/Application Support/PhpStorm2019.2 Library/Application Support/PhpStorm2019.3 Library/Application Support/PhpStorm2019.4 Library/Application Support/WebIde100 Library/Application Support/WebIde60 Library/Application Support/WebIde70 Library/Application Support/WebIde80 Library/Application Support/WebIde90 Library/Application Support/WebIde95 Library/Preferences/com.jetbrains.PhpStorm.plist Library/Preferences/PhpStorm2016.1 Library/Preferences/PhpStorm2016.2 Library/Preferences/PhpStorm2016.3 Library/Preferences/PhpStorm2017.1 Library/Preferences/PhpStorm2017.2 Library/Preferences/PhpStorm2017.3 Library/Preferences/PhpStorm2018.1 Library/Preferences/PhpStorm2018.2 Library/Preferences/PhpStorm2018.3 Library/Preferences/PhpStorm2019.1 Library/Preferences/PhpStorm2019.2 Library/Preferences/PhpStorm2019.3 Library/Preferences/PhpStorm2019.4 Library/Preferences/WebIde100 Library/Preferences/WebIde60 Library/Preferences/WebIde70 Library/Preferences/WebIde80 Library/Preferences/WebIde90 Library/Preferences/WebIde95 .PhpStorm2016.1/config .PhpStorm2016.2/config .PhpStorm2016.3/config .PhpStorm2017.1/config .PhpStorm2017.2/config .PhpStorm2017.3/config .PhpStorm2018.1/config .PhpStorm2018.2/config .PhpStorm2018.3/config .PhpStorm2019.1/config .PhpStorm2019.2/config .PhpStorm2019.3/config ================================================ FILE: src/mackup/applications/picgo.cfg ================================================ [application] name = PicGo [configuration_files] Library/Application Support/picgo/data.json ================================================ FILE: src/mackup/applications/pidgin.cfg ================================================ [application] name = Pidgin [configuration_files] .purple ================================================ FILE: src/mackup/applications/pip.cfg ================================================ [application] name = pip [configuration_files] Library/Application Support/pip/pip.conf .pip/pip.conf [xdg_configuration_files] pip/pip.conf ================================================ FILE: src/mackup/applications/pixelsnap.cfg ================================================ [application] name = PixelSnap [configuration_files] Library/Preferences/com.getpixelsnap.app.plist ================================================ FILE: src/mackup/applications/pixelsnap2.cfg ================================================ [application] name = PixelSnap 2 [configuration_files] Library/Preferences/pl.maketheweb.pixelsnap2.plist ================================================ FILE: src/mackup/applications/planner.cfg ================================================ [application] name = Planner [configuration_files] .var/app/com.github.alainm23.planner/config .var/app/com.github.alainm23.planner/data ================================================ FILE: src/mackup/applications/plover.cfg ================================================ [application] name = Plover [configuration_files] Library/Application Support/Plover ================================================ FILE: src/mackup/applications/pnpm.cfg ================================================ [application] name = pnpm [configuration_files] .pnpm/global_pnpmfile.js .pnpm-store/2/store.json ================================================ FILE: src/mackup/applications/pock.cfg ================================================ [application] name = Pock [configuration_files] Library/Preferences/com.pigigaldi.pock.plist ================================================ FILE: src/mackup/applications/podman.cfg ================================================ [application] name = Podman [xdg_configuration_files] containers/podman-connections.json ================================================ FILE: src/mackup/applications/poedit.cfg ================================================ [application] name = Poedit [configuration_files] Library/Preferences/net.poedit.Poedit.cfg Library/Preferences/net.poedit.Poedit.plist ================================================ FILE: src/mackup/applications/poetry.cfg ================================================ [application] name = poetry [configuration_files] Library/Application Support/pypoetry/config.toml Library/Preferences/pypoetry/config.toml [xdg_configuration_files] pypoetry/config.toml ================================================ FILE: src/mackup/applications/pokerstars.cfg ================================================ [application] name = PokerStars [configuration_files] Library/Application Support/PokerStars Library/Preferences/com.pokerstars.user.ini Library/Preferences/com.pokerstars.PokerStars.plist Library/Application Support/PokerStarsEU Library/Preferences/com.pokerstars.eu.user.ini Library/Preferences/com.pokerstars.PokerStarsEU.plist ================================================ FILE: src/mackup/applications/polybar.cfg ================================================ [application] name = polybar [configuration_files] .polybar/config .polybar/launch.sh [xdg_configuration_files] polybar/config polybar/launch.sh polybar/scripts ================================================ FILE: src/mackup/applications/popclip.cfg ================================================ [application] name = PopClip [configuration_files] Library/Preferences/com.pilotmoon.popclip.plist Library/Application Support/PopClip/Extensions ================================================ FILE: src/mackup/applications/popcorn-time.cfg ================================================ [application] name = Popcorn-Time [configuration_files] Library/Application Support/Popcorn-Time/data/bookmarks.db Library/Application Support/Popcorn-Time/data/settings.db Library/Application Support/Popcorn-Time/data/watched.db Library/Application Support/Popcorn-Time/data/movies.db Library/Application Support/Popcorn-Time/data/shows.db ================================================ FILE: src/mackup/applications/postico.cfg ================================================ [application] name = Postico [configuration_files] Library/Containers/at.eggerapps.Postico/Data/Library/Application Support/Postico Library/Containers/at.eggerapps.Postico/Data/Library/Preferences/at.eggerapps.Postico.plist Library/Preferences/at.eggerapps.Postico.plist ================================================ FILE: src/mackup/applications/pow.cfg ================================================ [application] name = Pow [configuration_files] .powconfig .powenv .powrc ================================================ FILE: src/mackup/applications/powerline.cfg ================================================ [application] name = Powerline [xdg_configuration_files] powerline/config.json powerline/colors.json powerline/themes powerline/colorschemes ================================================ FILE: src/mackup/applications/prezto.cfg ================================================ [application] name = Prezto [configuration_files] .zpreztorc ================================================ FILE: src/mackup/applications/processing.cfg ================================================ [application] name = Processing [configuration_files] Library/Processing/preferences.txt ================================================ FILE: src/mackup/applications/proselint.cfg ================================================ [application] name = proselint [configuration_files] .proselintrc [xdg_configuration_files] proselint/config ================================================ FILE: src/mackup/applications/proxychains.cfg ================================================ [application] name = ProxyChains [configuration_files] .proxychains ================================================ FILE: src/mackup/applications/proxyman.cfg ================================================ [application] name = Proxyman [configuration_files] Library/Application Support/com.proxyman.NSProxy Library/Preferences/com.proxyman.NSProxy.plist Library/Application Support/com.proxyman.NSProxy-setapp Library/Preferences/com.proxyman.NSProxy-setapp.plist ================================================ FILE: src/mackup/applications/prusa-slicer.cfg ================================================ [application] name = PrusaSlicer [xdg_configuration_files] PrusaSlicer ================================================ FILE: src/mackup/applications/psysh.cfg ================================================ [application] name = PsySH [xdg_configuration_files] psysh/config.php ================================================ FILE: src/mackup/applications/punto-switcher.cfg ================================================ [application] name = Punto Switcher [configuration_files] Library/Preferences/ru.yandex.punto.plist ================================================ FILE: src/mackup/applications/pycharm.cfg ================================================ [application] name = PyCharm [configuration_files] .PyCharm2016.1/config .PyCharm2016.2/config .PyCharm2016.3/config .PyCharm2017.1/config .PyCharm2017.2/config .PyCharm2017.3/config .PyCharm40/config .PyCharm50/config .PyCharmCE2016.1/config .PyCharmCE2016.2/config .PyCharmCE2016.3/config .PyCharmCE2017.1/config .PyCharmCE2017.2/config .PyCharmCE2017.3/config .PyCharmCE2019.3/config Library/Application Support/JetBrains/PyCharm2020.1 Library/Application Support/JetBrains/PyCharm2020.2 Library/Application Support/JetBrains/PyCharm2020.3 Library/Application Support/JetBrains/PyCharm2020.4 Library/Application Support/JetBrains/PyCharm2023.1 Library/Application Support/JetBrains/PyCharm2023.2 Library/Application Support/JetBrains/PyCharmCE2020.1 Library/Application Support/PyCharm Library/Application Support/PyCharm2016.1 Library/Application Support/PyCharm2016.2 Library/Application Support/PyCharm2016.3 Library/Application Support/PyCharm2017.1 Library/Application Support/PyCharm2017.2 Library/Application Support/PyCharm2017.3 Library/Application Support/PyCharm2019.2 Library/Application Support/PyCharm2019.3 Library/Application Support/PyCharm40 Library/Application Support/PyCharm50 Library/Application Support/PyCharmCE2016.1 Library/Application Support/PyCharmCE2016.2 Library/Application Support/PyCharmCE2016.3 Library/Application Support/PyCharmCE2017.1 Library/Application Support/PyCharmCE2017.2 Library/Application Support/PyCharmCE2017.3 Library/Preferences/PyCharm2016.1 Library/Preferences/PyCharm2016.2 Library/Preferences/PyCharm2016.3 Library/Preferences/PyCharm2017.1 Library/Preferences/PyCharm2017.2 Library/Preferences/PyCharm2017.3 Library/Preferences/PyCharm2019.2 Library/Preferences/PyCharm2019.3 Library/Preferences/PyCharm40 Library/Preferences/PyCharm50 Library/Preferences/PyCharmCE2016.1 Library/Preferences/PyCharmCE2016.2 Library/Preferences/PyCharmCE2016.3 Library/Preferences/PyCharmCE2017.1 Library/Preferences/PyCharmCE2017.2 Library/Preferences/PyCharmCE2017.3 ================================================ FILE: src/mackup/applications/pypi.cfg ================================================ [application] name = PyPI [configuration_files] .pypirc ================================================ FILE: src/mackup/applications/pyradio.cfg ================================================ [application] name = PyRadio [configuration_files] .pyradio/stations.csv ================================================ FILE: src/mackup/applications/querious.cfg ================================================ [application] name = Querious [configuration_files] Library/Preferences/com.araeliumgroup.querious.plist Library/Application Support/Querious ================================================ FILE: src/mackup/applications/quicklook.cfg ================================================ [application] name = Quicklook [configuration_files] Library/Quicklook ================================================ FILE: src/mackup/applications/quicksilver.cfg ================================================ [application] name = Quicksilver [configuration_files] Library/Preferences/com.blacktree.Quicksilver.plist Library/Application Support/Quicksilver ================================================ FILE: src/mackup/applications/quitter.cfg ================================================ [application] name = Quitter [configuration_files] Library/Preferences/com.marcoarment.quitter.plist ================================================ FILE: src/mackup/applications/qutebrowser.cfg ================================================ [application] name = Qutebrowser [configuration_files] .qutebrowser/config.py [xdg_configuration_files] qutebrowser/config.py qutebrowser/qutebrowser.conf ================================================ FILE: src/mackup/applications/qv2ray.cfg ================================================ [application] name = Qv2ray [configuration_files] Library/Preferences/qv2ray/Qv2ray.conf Library/Preferences/qv2ray/plugin_settings ================================================ FILE: src/mackup/applications/r.cfg ================================================ [application] name = R [configuration_files] .R .Rhistory .Rprofile .Rprofile.d .Renviron .Renviron.d Library/Preferences/org.R-project.R.plist ================================================ FILE: src/mackup/applications/rails.cfg ================================================ [application] name = Rails [configuration_files] .railsrc ================================================ FILE: src/mackup/applications/ranger.cfg ================================================ [application] name = Ranger [xdg_configuration_files] ranger/commands.py ranger/rc.conf ranger/rifle.conf ranger/scope.sh ranger/plugins ================================================ FILE: src/mackup/applications/rbenv.cfg ================================================ [application] name = rbenv [configuration_files] .rbenv/default_gems ================================================ FILE: src/mackup/applications/rclone.cfg ================================================ [application] name = rclone [xdg_configuration_files] rclone ================================================ FILE: src/mackup/applications/rectangle.cfg ================================================ [application] name = Rectangle [configuration_files] Library/Preferences/com.knollsoft.Rectangle.plist ================================================ FILE: src/mackup/applications/redshift-scheduler.cfg ================================================ [application] name = Redshift Scheduler [xdg_configuration_files] redshift-scheduler/rules.conf ================================================ FILE: src/mackup/applications/redshift.cfg ================================================ [application] name = Redshift [xdg_configuration_files] redshift.conf ================================================ FILE: src/mackup/applications/remote-desktop-manager.cfg ================================================ [application] name = Remote Desktop Manager [configuration_files] Library/Application Support/com.devolutions.remotedesktopmanager.free/Connections.db Library/Application Support/com.devolutions.remotedesktopmanager/Connections.db ================================================ FILE: src/mackup/applications/rhythmbox.cfg ================================================ [application] name = Rhythmbox [configuration_files] .gconf/apps/rhythmbox ================================================ FILE: src/mackup/applications/rime.cfg ================================================ [application] name = Rime [configuration_files] Library/Rime/default.custom.yaml Library/Rime/default.yaml Library/Rime/installation.yaml Library/Rime/squirrel.custom.yaml Library/Rime/squirrel.yaml Library/Rime/symbols.yaml Library/Rime/user.yaml [xdg_configuration_files] ibus/rimedefault.custom.yaml ibus/rimedefault.yaml ibus/rimeinstallation.yaml ibus/rimesquirrel.custom.yaml ibus/rimesquirrel.yaml ibus/rimesymbols.yaml ibus/rimeuser.yaml ================================================ FILE: src/mackup/applications/ripgrep.cfg ================================================ [application] name = ripgrep [configuration_files] .ripgreprc ================================================ FILE: src/mackup/applications/robo3t.cfg ================================================ [application] name = Robo 3T [configuration_files] Library/Preferences/com.3t.Robomongo.plist .3T ================================================ FILE: src/mackup/applications/rocket.cfg ================================================ [application] name = Rocket [configuration_files] Library/Preferences/net.matthewpalmer.Rocket.plist Library/Application Support/Rocket ================================================ FILE: src/mackup/applications/rofi.cfg ================================================ [application] name = rofi [xdg_configuration_files] rofi/config.rasi ================================================ FILE: src/mackup/applications/royaltsx.cfg ================================================ [application] name = Royal TSX [configuration_files] Library/Application Support/Royal TSX/License.xml Library/Application Support/Royal TSX/UserPreferences.config Library/Preferences/com.lemonmojo.RoyalTSX.App.plist ================================================ FILE: src/mackup/applications/rstudio.cfg ================================================ [application] name = RStudio [configuration_files] .rstudio-desktop Library/Preferences/org.rstudio.RStudio.plist [xdg_configuration_files] rstudio ================================================ FILE: src/mackup/applications/rtorrent.cfg ================================================ [application] name = rTorrent [configuration_files] .rtorrent.rc ================================================ FILE: src/mackup/applications/rtx.cfg ================================================ [application] name = rtx [xdg_configuration_files] rtx [configuration_files] .rtx.toml .tool-versions .default-go-packages .default-gems .default-nodejs-packages .default-node-packages .default-npm-packages .default-python-packages .default-mix-commands ================================================ FILE: src/mackup/applications/rubitrack5.cfg ================================================ [application] name = rubiTrack 5 [configuration_files] Library/Preferences/com.shiftoption.rubitrack5.pro.plist ================================================ FILE: src/mackup/applications/rubocop.cfg ================================================ [application] name = Rubocop [configuration_files] .rubocop.yml ================================================ FILE: src/mackup/applications/ruby-version.cfg ================================================ [application] name = Ruby Version [configuration_files] .ruby-version ================================================ FILE: src/mackup/applications/ruby.cfg ================================================ [application] name = Ruby [configuration_files] .gemrc .irbrc .gem/credentials .pryrc .aprc ================================================ FILE: src/mackup/applications/rubymine.cfg ================================================ [application] name = RubyMine [configuration_files] Library/Application Support/RubyMine40 Library/Preferences/RubyMine40 Library/Application Support/RubyMine50 Library/Preferences/RubyMine50 Library/Application Support/RubyMine60 Library/Preferences/RubyMine60 Library/Application Support/RubyMine70 Library/Preferences/RubyMine70 Library/Application Support/RubyMine80 Library/Preferences/RubyMine80 Library/Application Support/RubyMine2016.1 Library/Preferences/RubyMine2016.1 Library/Application Support/RubyMine2016.2 Library/Preferences/RubyMine2016.2 Library/Application Support/RubyMine2016.3 Library/Preferences/RubyMine2016.3 Library/Application Support/RubyMine2017.3 Library/Preferences/RubyMine2017.3 Library/Application Support/RubyMine2018.1 Library/Preferences/RubyMine2018.1 Library/Application Support/JetBrains/RubyMine2023.1 Library/Application Support/JetBrains/RubyMine2023.2 ================================================ FILE: src/mackup/applications/rust.cfg ================================================ [application] name = Rust [configuration_files] .cargo/config.toml .cargo/config ================================================ FILE: src/mackup/applications/rustrover.cfg ================================================ [application] name = RustRover [configuration_files] Library/Preferences/RustRover2025.2 Library/Preferences/RustRover2025.3 Library/Application Support/JetBrains/RustRover2025.2 Library/Application Support/JetBrains/RustRover2025.3 ================================================ FILE: src/mackup/applications/rvm.cfg ================================================ [application] name = Ruby Version Manager [configuration_files] .rvmrc ================================================ FILE: src/mackup/applications/s3cmd.cfg ================================================ [application] name = S3cmd [configuration_files] .s3cfg ================================================ FILE: src/mackup/applications/sabnzbd.cfg ================================================ [application] name = SABnzbd [configuration_files] Library/Application Support/SABnzbd/sabnzbd.ini Library/Application Support/SABnzbd/admin/rss_data.sab ================================================ FILE: src/mackup/applications/sbcl.cfg ================================================ [application] name = SBCL [configuration_files] .sbclrc ================================================ FILE: src/mackup/applications/sbt.cfg ================================================ [application] name = SBT [configuration_files] .sbtconfig .sbt/0.12/global.sbt .sbt/0.12/build.sbt .sbt/0.12/plugins/plugins.sbt .sbt/0.12/plugins/build.sbt .sbt/0.13/global.sbt .sbt/0.13/build.sbt .sbt/0.13/plugins/plugins.sbt .sbt/0.13/plugins/build.sbt .sbt/1.0/global.sbt .sbt/1.0/build.sbt .sbt/1.0/plugins/plugins.sbt .sbt/1.0/plugins/build.sbt ================================================ FILE: src/mackup/applications/scenario.cfg ================================================ [application] name = Scenario [configuration_files] Library/Preferences/com.lagente.scenario.plist Library/Scenario ================================================ FILE: src/mackup/applications/screen.cfg ================================================ [application] name = Screen [configuration_files] .screenrc ================================================ FILE: src/mackup/applications/screenhero.cfg ================================================ [application] name = Screenhero [configuration_files] Library/Preferences/com.screenhero.screenhero.plist ================================================ FILE: src/mackup/applications/scrivener.cfg ================================================ [application] name = Scrivener [configuration_files] Library/Preferences/com.literatureandlatte.scrivener2.plist Library/Application Support/Scrivener ================================================ FILE: src/mackup/applications/scroll-reverser.cfg ================================================ [application] name = Scroll Reverser [configuration_files] Library/Preferences/com.pilotmoon.scroll-reverser.plist ================================================ FILE: src/mackup/applications/secure-pipes.cfg ================================================ [application] name = Secure Pipes [configuration_files] Library/Preferences/net.edgeservices.connections.plist Library/Preferences/net.edgeservices.Secure-Pipes.plist Library/Preferences/net.edgeservices.sp-config.plist Library/Application Support/Secure Pipes ================================================ FILE: src/mackup/applications/securecrt.cfg ================================================ [application] name = SecureCRT [configuration_files] Library/Application Support/VanDyke/SecureCRT/Config Library/Preferences/com.vandyke.SecureCRT.plist ================================================ FILE: src/mackup/applications/seil.cfg ================================================ [application] name = Seil [configuration_files] Library/Preferences/org.pqrs.PCKeyboardHack.plist Library/Preferences/org.pqrs.Seil.plist ================================================ FILE: src/mackup/applications/selfcontrol.cfg ================================================ [application] name = SelfControl [configuration_files] Library/Preferences/org.eyebeam.SelfControl.plist ================================================ FILE: src/mackup/applications/sequel-pro.cfg ================================================ [application] name = Sequel Pro [configuration_files] Library/Application Support/Sequel Pro/Data Library/Application Support/Sequel Pro/Bundles Library/Application Support/Sequel Pro/Themes Library/Preferences/com.sequelpro.SequelPro.plist ================================================ FILE: src/mackup/applications/shadowsocksx-ng.cfg ================================================ [application] name = ShadowsocksX-NG [configuration_files] .ShadowsocksX-NG ================================================ FILE: src/mackup/applications/shiftit.cfg ================================================ [application] name = ShiftIt [configuration_files] Library/Preferences/org.shiftitapp.ShiftIt.plist ================================================ FILE: src/mackup/applications/shifty.cfg ================================================ [application] name = Shifty [configuration_files] Library/Preferences/io.natethompson.Shifty.plist ================================================ FILE: src/mackup/applications/shimo.cfg ================================================ [application] name = Shimo [configuration_files] Library/Application Support/Shimo/ShimoProfiles.xml Library/Preferences/com.chungwasoft.Shimo.plist ================================================ FILE: src/mackup/applications/showyedge.cfg ================================================ [application] name = ShowyEdge [configuration_files] Library/Preferences/org.pqrs.ShowyEdge.plist ================================================ FILE: src/mackup/applications/shsh-blobs.cfg ================================================ [application] name = SHSH Blobs [configuration_files] .shsh ================================================ FILE: src/mackup/applications/shuttle.cfg ================================================ [application] name = Shuttle [configuration_files] .shuttle.json ================================================ FILE: src/mackup/applications/sizeup.cfg ================================================ [application] name = SizeUp [configuration_files] Library/Preferences/com.irradiatedsoftware.SizeUp.plist Library/Application Support/SizeUp/SizeUp.sizeuplicense ================================================ FILE: src/mackup/applications/sizzy.cfg ================================================ [application] name = Sizzy [configuration_files] Library/Application Support/Sizzy/config.json Library/Application Support/Sizzy/window-state.json ================================================ FILE: src/mackup/applications/sketchybar.cfg ================================================ [application] name = SketchyBar [xdg_configuration_files] sketchybar/sketchybarrc sketchybar/plugins ================================================ FILE: src/mackup/applications/skhd.cfg ================================================ [application] name = skhd [configuration_files] .skhdrc [xdg_configuration_files] skhd/skhdrc ================================================ FILE: src/mackup/applications/skim.cfg ================================================ [application] name = Skim [configuration_files] Library/Preferences/net.sourceforge.skim-app.skim.plist ================================================ FILE: src/mackup/applications/skitch.cfg ================================================ [application] name = Skitch [configuration_files] Library/Preferences/com.plasq.skitch.plist Library/Preferences/com.plasq.skitch.history ================================================ FILE: src/mackup/applications/slate.cfg ================================================ [application] name = Slate [configuration_files] .slate .slate.js Library/Application Support/com.slate.Slate ================================================ FILE: src/mackup/applications/slic3r.cfg ================================================ [application] name = Slic3r [configuration_files] Library/Application Support/Slic3r ================================================ FILE: src/mackup/applications/slogger.cfg ================================================ [application] name = Slogger [configuration_files] Slogger ================================================ FILE: src/mackup/applications/smartgit.cfg ================================================ [application] name = SmartGit [configuration_files] Library/Preferences/SmartGit/smartgit.vmoptions ================================================ FILE: src/mackup/applications/smooth-mouse.cfg ================================================ [application] name = Smooth Mouse [configuration_files] Library/Preferences/com.cyberic.SmoothMouse.plist ================================================ FILE: src/mackup/applications/soulver.cfg ================================================ [application] name = Soulver [configuration_files] Library/Application Support/Soulver Library/Preferences/com.acqualia.soulver.plist # Soulver 3 Library/Application Support/Soulver 3 Library/Preferences/app.soulver.mac.plist ================================================ FILE: src/mackup/applications/sourcetree.cfg ================================================ [application] name = SourceTree [configuration_files] Library/Application Support/SourceTree/sourcetree.license Library/Application Support/SourceTree/browser.plist Library/Application Support/SourceTree/hgrc_sourcetree Library/Application Support/SourceTree/hostingservices.plist ================================================ FILE: src/mackup/applications/spacelauncher.cfg ================================================ [application] name = SpaceLauncher [configuration_files] Library/Preferences/name.guoc.SpaceLauncher.plist ================================================ FILE: src/mackup/applications/spacemacs.cfg ================================================ [application] name = Spacemacs [configuration_files] .spacemacs .spacemacs.d ================================================ FILE: src/mackup/applications/spacevim.cfg ================================================ [application] name = SpaceVim [configuration_files] # Default configuration directory .SpaceVim.d # Default configuration file; could be this specific or backup the config directory # .SpaceVim.d/init.toml ================================================ FILE: src/mackup/applications/spamsieve.cfg ================================================ [application] name = SpamSieve [configuration_files] Library/Application Support/SpamSieve Library/Preferences/com.c-command.SpamSieve.plist ================================================ FILE: src/mackup/applications/spark.cfg ================================================ [application] name = Spark [configuration_files] Library/Application Support/Spark ================================================ FILE: src/mackup/applications/spectacle.cfg ================================================ [application] name = Spectacle [configuration_files] Library/Preferences/com.divisiblebyzero.Spectacle.plist Library/Application Support/Spectacle ================================================ FILE: src/mackup/applications/spectrwm.cfg ================================================ [application] name = Spectrwm [configuration_files] .spectrwm.conf ================================================ FILE: src/mackup/applications/splice.cfg ================================================ [application] name = Splice [configuration_files] Library/Preferences/com.splice.Splice.plist ================================================ FILE: src/mackup/applications/spotify-notifications.cfg ================================================ [application] name = Spotify-Notifications [configuration_files] Library/Preferences/io.citruspi.Spotify-Notifications.plist ================================================ FILE: src/mackup/applications/spotify.cfg ================================================ [application] name = Spotify [configuration_files] Library/Preferences/com.spotify.client.plist ================================================ FILE: src/mackup/applications/sqitch.cfg ================================================ [application] name = Sqitch [configuration_files] .sqitch/sqitch.conf ================================================ FILE: src/mackup/applications/ssh.cfg ================================================ [application] name = SSH [configuration_files] .ssh/config .ssh/authorized_keys ================================================ FILE: src/mackup/applications/starship.cfg ================================================ [application] name = Starship [configuration_files] # .config is hardcoded, see https://github.com/starship/starship/blob/1eabd527252291398df5749d30f3459ea2a03823/src/config.rs#L184 .config/starship.toml ================================================ FILE: src/mackup/applications/startupizer2.cfg ================================================ [application] name = Startupizer2 [configuration_files] Library/Application Support/Startupizer ================================================ FILE: src/mackup/applications/stata.cfg ================================================ [application] name = Stata [configuration_files] Library/Application Support/Stata Library/Preferences/com.stata.stata12.plist Library/Preferences/com.stata.stata13.plist Library/Preferences/Stata 13 Preferences ================================================ FILE: src/mackup/applications/stats.cfg ================================================ [application] name = Stats [configuration_files] Library/Preferences/eu.exelban.Stats.plist ================================================ FILE: src/mackup/applications/stay.cfg ================================================ [application] name = Stay [configuration_files] Library/Preferences/com.cordlessdog.Stay.plist Library/Application Support/Stay ================================================ FILE: src/mackup/applications/storyist-3.cfg ================================================ [application] name = storyist-3 [configuration_files] Library/Preferences/.com.storyist.storyist.plist Library/Preferences/com.storyist.storyist.plist ================================================ FILE: src/mackup/applications/streamdeck.cfg ================================================ [application] name = Elgato StreamDeck [configuration_files] Library/Application Support/com.elgato.StreamDeck ================================================ FILE: src/mackup/applications/subler.cfg ================================================ [application] name = Subler [configuration_files] Library/Application Support/Subler ================================================ FILE: src/mackup/applications/sublime-merge.cfg ================================================ [application] name = Sublime Merge [configuration_files] Library/Application Support/Sublime Merge/Packages/User ================================================ FILE: src/mackup/applications/sublime-text-2.cfg ================================================ [application] name = Sublime Text 2 [configuration_files] # Based on https://packagecontrol.io/docs/syncing Library/Application Support/Sublime Text 2/Packages/User [xdg_configuration_files] sublime-text-2/Packages/User ================================================ FILE: src/mackup/applications/sublime-text-3.cfg ================================================ [application] name = Sublime Text 3 [configuration_files] # Based on https://packagecontrol.io/docs/syncing Library/Application Support/Sublime Text 3/Packages/User [xdg_configuration_files] sublime-text-3/Packages/User ================================================ FILE: src/mackup/applications/sublime-text.cfg ================================================ [application] name = Sublime Text [configuration_files] # Based on https://packagecontrol.io/docs/syncing Library/Application Support/Sublime Text/Packages/User [xdg_configuration_files] sublime-text/Packages/User ================================================ FILE: src/mackup/applications/subversion.cfg ================================================ [application] name = Subversion [configuration_files] .subversion ================================================ FILE: src/mackup/applications/superduper.cfg ================================================ [application] name = SuperDuper! [configuration_files] Library/Application Support/SuperDuper! ================================================ FILE: src/mackup/applications/surge.cfg ================================================ [application] name = Surge [configuration_files] .surge.conf ================================================ FILE: src/mackup/applications/swaywm.cfg ================================================ [application] name = Swaywm [xdg_configuration_files] sway ================================================ FILE: src/mackup/applications/swinsian.cfg ================================================ [application] name = Swinsian [configuration_files] Library/Application Support/Swinsian/Library.sqlite Library/Application Support/Swinsian/License.swinsianlicense Library/Preferences/com.swinsian.Swinsian.plist Library/Preferences/com.swinsian.SwinsianDesktopWindow.plist ================================================ FILE: src/mackup/applications/swish.cfg ================================================ [application] name = Swish [configuration_files] Library/Preferences/co.highlyopinionated.swish.plist ================================================ FILE: src/mackup/applications/switchhosts.cfg ================================================ [application] name = SwitchHosts [configuration_files] .SwitchHosts/data/collection/hosts ================================================ FILE: src/mackup/applications/t.cfg ================================================ [application] name = t [configuration_files] .trc ================================================ FILE: src/mackup/applications/tableplus.cfg ================================================ [application] name = TablePlus [configuration_files] Library/Application Support/com.tinyapp.TablePlus/Cache/Favorite Library/Application Support/com.tinyapp.TablePlus/Cache/History Library/Application Support/com.tinyapp.TablePlus/Data Library/Application Support/com.tinyapp.TablePlus/Plugins Library/Preferences/com.tinyapp.TablePlus.plist ================================================ FILE: src/mackup/applications/taskpaper.cfg ================================================ [application] name = Task Paper [configuration_files] Library/Preferences/com.hogbaysoftware.TaskPaper.mac.plist Library/Application Support/TaskPaper ================================================ FILE: src/mackup/applications/taskwarrior.cfg ================================================ [application] name = Taskwarrior [configuration_files] .taskrc ## Note: taskwarrior has it's own sync feature for actual tasks: ## http://taskwarrior.org/docs/taskserver/setup.html ## ## If you'd rather want to sync using mackup, uncomment the following lines. # .task ================================================ FILE: src/mackup/applications/teamocil.cfg ================================================ [application] name = Teamocil [configuration_files] .teamocil ================================================ FILE: src/mackup/applications/telegram_macos.cfg ================================================ [application] name = Telegram for macOS [configuration_files] Library/Preferences/ru.keepcoder.Telegram.plist ================================================ FILE: src/mackup/applications/terminal.cfg ================================================ [application] name = Terminal [configuration_files] Library/Preferences/com.apple.Terminal.plist ================================================ FILE: src/mackup/applications/terminator.cfg ================================================ [application] name = Terminator [xdg_configuration_files] terminator/config ================================================ FILE: src/mackup/applications/termite.cfg ================================================ [application] name = termite [configuration_files] termite/config ================================================ FILE: src/mackup/applications/termux.cfg ================================================ [application] name = Configuration for Termux [configuration_files] .termux/colors.properties .termux/termux.properties .termux/font.ttf ================================================ FILE: src/mackup/applications/terraform.cfg ================================================ [application] name = Terraform [configuration_files] .terraformrc .terraform.d ================================================ FILE: src/mackup/applications/textexpander.cfg ================================================ [application] name = TextExpander [configuration_files] Library/Application Support/TextExpander/Settings.textexpander Library/Preferences/com.smileonmymac.textexpander.plist ================================================ FILE: src/mackup/applications/textmate.cfg ================================================ [application] name = TextMate [configuration_files] Library/Application Support/TextMate/Bundles Library/Application Support/TextMate/PlugIns Library/Application Support/TextMate/Pristine Copy Library/Application Support/TextMate/Managed/Bundles Library/Preferences/com.macromates.textmate.latex_config.plist .tm_properties ================================================ FILE: src/mackup/applications/textual.cfg ================================================ [application] name = Textual [configuration_files] Library/Application Support/Textual IRC Library/Preferences/com.codeux.irc.textual.plist Library/Containers/com.codeux.irc.textual/Data/Library/Preferences/com.codeux.irc.textual.plist ================================================ FILE: src/mackup/applications/things.cfg ================================================ [application] name = Things [configuration_files] Library/Preferences/com.culturedcode.things.plist Library/Application Support/Cultured Code/Licenses.plist ================================================ FILE: src/mackup/applications/tidy.cfg ================================================ [application] name = HTML Tidy [configuration_files] .tidyrc ================================================ FILE: src/mackup/applications/tig.cfg ================================================ [application] name = Tig [configuration_files] .tigrc [xdg_configuration_files] tig/config ================================================ FILE: src/mackup/applications/tiles.cfg ================================================ [application] name = Tiles [configuration_files] Library/Preferences/com.sempliva.Tiles.plist ================================================ FILE: src/mackup/applications/tilix.cfg ================================================ [application] name = tilix [configuration_files] tilix.dconf ================================================ FILE: src/mackup/applications/timeout.cfg ================================================ [application] name = TimeOut [configuration_files] Library/Group Containers/6Z7QW53WB6.com.dejal.timeout ================================================ FILE: src/mackup/applications/tint2.cfg ================================================ [application] name = tint2 [xdg_configuration_files] tint2/tint2rc ================================================ FILE: src/mackup/applications/tinyfugue.cfg ================================================ [application] name = TinyFugue [configuration_files] .tfrc ================================================ FILE: src/mackup/applications/tmux.cfg ================================================ [application] name = Tmux [configuration_files] .tmux.conf [xdg_configuration_files] tmux/tmux.conf ================================================ FILE: src/mackup/applications/tmuxinator.cfg ================================================ [application] name = Tmuxinator [xdg_configuration_files] tmuxinator ================================================ FILE: src/mackup/applications/tmuxp.cfg ================================================ [application] name = tmuxp [configuration_files] .tmuxp ================================================ FILE: src/mackup/applications/todotxt-cli.cfg ================================================ [application] name = Todo.txt CLI [configuration_files] .todo/config ================================================ FILE: src/mackup/applications/toothfairy.cfg ================================================ [application] name = ToothFairy [configuration_files] Library/Application Scripts/com.robinlu.mac.Tooth-Fairy ================================================ FILE: src/mackup/applications/totalspaces2.cfg ================================================ [application] name = TotalSpaces2 [configuration_files] Library/Preferences/com.binaryage.TotalSpaces2.plist ================================================ FILE: src/mackup/applications/tower-2.cfg ================================================ [application] name = Tower 2 [configuration_files] Library/Application Support/com.fournova.Tower2 Library/Preferences/com.fournova.Tower2.plist ================================================ FILE: src/mackup/applications/tower-3.cfg ================================================ [application] name = Tower 3 [configuration_files] Library/Application Support/com.fournova.Tower3 Library/Preferences/com.fournova.Tower3.plist ================================================ FILE: src/mackup/applications/tower.cfg ================================================ [application] name = Tower [configuration_files] Library/Application Support/Tower Library/Application Support/com.fournova.Tower3 Library/Preferences/com.fournova.Tower.plist Library/Preferences/com.fournova.Tower3.plist ================================================ FILE: src/mackup/applications/transmission.cfg ================================================ [application] name = Transmission [configuration_files] Library/Preferences/org.m0k.transmission.plist Library/Application Support/Transmission/blocklists [xdg_configuration_files] transmission/blocklists transmission/settings.json transmission/stats.json transmission-daemon/blocklists transmission-daemon/settings.json transmission-daemon/stats.json ================================================ FILE: src/mackup/applications/transmit.cfg ================================================ [application] name = Transmit [configuration_files] Library/Preferences/com.panic.Transmit.plist Library/Application Support/Transmit/Metadata Library/Application Support/Transmit/Favorites ================================================ FILE: src/mackup/applications/tripmode.cfg ================================================ [application] name = TripMode [configuration_files] Library/Application Support/TripMode ================================================ FILE: src/mackup/applications/trizen.cfg ================================================ [application] name = Trizen [xdg_configuration_files] trizen/trizen.conf ================================================ FILE: src/mackup/applications/tunnelblick.cfg ================================================ [application] name = Tunnelblick [configuration_files] Library/Application Support/Tunnelblick/Configurations ================================================ FILE: src/mackup/applications/tvnamer.cfg ================================================ [application] name = tvnamer [configuration_files] .tvnamer.json ================================================ FILE: src/mackup/applications/twitterrific.cfg ================================================ [application] name = Twitterrific [configuration_files] Library/Application Support/Twitterrific ================================================ FILE: src/mackup/applications/typinator.cfg ================================================ [application] name = Typinator [configuration_files] Library/Application Support/Typinator Library/Preferences/com.macility.typinator2.plist ================================================ FILE: src/mackup/applications/typora.cfg ================================================ [application] name = Typora [configuration_files] Library/Application Support/abnerworks.Typora/themes [xdg_configuration_files] Typora/themes ================================================ FILE: src/mackup/applications/ubersicht.cfg ================================================ [application] name = Ubersicht [configuration_files] Library/Application Support/Übersicht/widgets Library/Preferences/tracesOf.Uebersicht.plist ================================================ FILE: src/mackup/applications/ulauncher.cfg ================================================ [application] name = Ulauncher [xdg_configuration_files] ulauncher/extensions.json ulauncher/settings.json ulauncher/shortcuts.json ================================================ FILE: src/mackup/applications/ventrilo.cfg ================================================ [application] name = Ventrilo [configuration_files] Library/Preferences/Ventrilo ================================================ FILE: src/mackup/applications/verdaccio.cfg ================================================ [application] name = Verdaccio [xdg_configuration_files] verdaccio/config.yaml ================================================ FILE: src/mackup/applications/versions.cfg ================================================ [application] name = Versions [configuration_files] Library/Application Support/Versions/License Library/Preferences/com.blackpixel.versions.plist ================================================ FILE: src/mackup/applications/vim.cfg ================================================ [application] name = Vim [configuration_files] .gvimrc .gvimrc.after .gvimrc.before .vim/autoload .vim/after .vim/bundle .vim/colors .vim/doc .vim/ftdetect .vim/ftplugin .vim/indent .vim/pack .vim/plugin/settings .vim/spell .vim/syntax .vim/vimrc .vimrc .vimrc.after .vimrc.before ================================================ FILE: src/mackup/applications/vimperator.cfg ================================================ [application] name = Vimperator [configuration_files] .vimperator .vimperatorrc ================================================ FILE: src/mackup/applications/vimwiki.cfg ================================================ [application] name = Vimwiki [configuration_files] vimwiki ================================================ FILE: src/mackup/applications/viscosity.cfg ================================================ [application] name = Viscosity [configuration_files] Library/Application Support/Viscosity/OpenVPN Library/Preferences/com.viscosityvpn.Viscosity.plist ================================================ FILE: src/mackup/applications/vlc.cfg ================================================ [application] name = VLC [configuration_files] Library/Application Support/org.videolan.vlc Library/Preferences/org.videolan.vlc Library/Preferences/org.videolan.vlc.LSSharedFileList.plist Library/Preferences/org.videolan.vlc.plist ================================================ FILE: src/mackup/applications/volt.cfg ================================================ [application] name = Volt [configuration_files] volt/lock.json volt/plugconf volt/rc ================================================ FILE: src/mackup/applications/vs4mac.cfg ================================================ [application] name = Visual Studio for Mac [configuration_files] Library/VisualStudio Library/Preferences/VisualStudio/7.0/EditingLayout.xml Library/Preferences/VisualStudio/7.0/MonoDevelopProperties.xml Library/Preferences/VisualStudio/8.0/EditingLayout.xml Library/Preferences/VisualStudio/8.0/MonoDevelopProperties.xml ================================================ FILE: src/mackup/applications/vscode-insiders.cfg ================================================ [application] name = Visual Studio Code Insiders [configuration_files] Library/Application Support/Code - Insiders/User/snippets Library/Application Support/Code - Insiders/User/prompts Library/Application Support/Code - Insiders/User/keybindings.json Library/Application Support/Code - Insiders/User/settings.json [xdg_configuration_files] Code - Insiders/User/snippets Code - Insiders/User/keybindings.json Code - Insiders/User/settings.json ================================================ FILE: src/mackup/applications/vscode-oss.cfg ================================================ [application] name = Visual Studio Code Open Source Edition [configuration_files] Library/Application Support/Code - OSS/User/snippets Library/Application Support/Code - OSS/User/keybindings.json Library/Application Support/Code - OSS/User/settings.json [xdg_configuration_files] Code - OSS/User/snippets Code - OSS/User/keybindings.json Code - OSS/User/settings.json ================================================ FILE: src/mackup/applications/vscode.cfg ================================================ [application] name = Visual Studio Code [configuration_files] Library/Application Support/Code/User/snippets Library/Application Support/Code/User/prompts Library/Application Support/Code/User/keybindings.json Library/Application Support/Code/User/settings.json [xdg_configuration_files] Code/User/snippets Code/User/keybindings.json Code/User/settings.json ================================================ FILE: src/mackup/applications/vscodium.cfg ================================================ [application] name = VSCodium [configuration_files] Library/Application Support/VSCodium/User/snippets Library/Application Support/VSCodium/User/keybindings.json Library/Application Support/VSCodium/User/settings.json [xdg_configuration_files] VSCodium/User/snippets VSCodium/User/keybindings.json VSCodium/User/settings.json ================================================ FILE: src/mackup/applications/wakatime.cfg ================================================ [application] name = Wakatime [configuration_files] .wakatime.cfg ================================================ FILE: src/mackup/applications/warp.cfg ================================================ [application] name = Warp [configuration_files] .warp ================================================ FILE: src/mackup/applications/waybar.cfg ================================================ [application] name = waybar [xdg_configuration_files] waybar ================================================ FILE: src/mackup/applications/webstorm.cfg ================================================ [application] name = WebStorm [configuration_files] Library/Application Support/JetBrains/WebStorm2020.1 Library/Application Support/JetBrains/WebStorm2020.2 Library/Application Support/JetBrains/WebStorm2020.3 Library/Application Support/JetBrains/WebStorm2020.4 Library/Application Support/JetBrains/WebStorm2021.1 Library/Application Support/JetBrains/WebStorm2021.2 Library/Application Support/JetBrains/WebStorm2021.3 Library/Application Support/JetBrains/WebStorm2023.1 Library/Application Support/JetBrains/WebStorm2023.2 Library/Application Support/WebStorm Library/Application Support/WebStorm10 Library/Application Support/WebStorm11 Library/Application Support/WebStorm2016.1 Library/Application Support/WebStorm2016.2 Library/Application Support/WebStorm2016.3 Library/Application Support/WebStorm2017.1 Library/Application Support/WebStorm2017.2 Library/Application Support/WebStorm2017.3 Library/Application Support/WebStorm2018.1 Library/Application Support/WebStorm2018.2 Library/Application Support/WebStorm2018.3 Library/Application Support/WebStorm2019.1 Library/Application Support/WebStorm2019.2 Library/Application Support/WebStorm2019.3 Library/Application Support/WebStorm2019.4 Library/Application Support/WebStorm8 Library/Application Support/WebStorm9 Library/Preferences/WebStorm10 Library/Preferences/WebStorm11 Library/Preferences/WebStorm2016.1 Library/Preferences/WebStorm2016.2 Library/Preferences/WebStorm2016.3 Library/Preferences/WebStorm2017.1 Library/Preferences/WebStorm2017.2 Library/Preferences/WebStorm2017.3 Library/Preferences/WebStorm2018.1 Library/Preferences/WebStorm2018.2 Library/Preferences/WebStorm2018.3 Library/Preferences/WebStorm2019.1 Library/Preferences/WebStorm2019.2 Library/Preferences/WebStorm2019.3 Library/Preferences/WebStorm2019.4 Library/Preferences/WebStorm8 Library/Preferences/WebStorm9 ================================================ FILE: src/mackup/applications/wezterm.cfg ================================================ [application] name = WezTerm [xdg_configuration_files] wezterm [configuration_files] .wezterm.lua ================================================ FILE: src/mackup/applications/wget.cfg ================================================ [application] name = Wget [configuration_files] .wgetrc .wget-hsts ================================================ FILE: src/mackup/applications/whatsapp.cfg ================================================ [application] name = WhatsApp Web [configuration_files] Library/Application Support/WhatsApp/Preferences Library/Application Support/WhatsApp/settings.json Library/Preferences/WhatsApp-Helper.plist Library/Preferences/WhatsApp.plist ================================================ FILE: src/mackup/applications/windsurf.cfg ================================================ [application] name = Windsurf [configuration_files] Library/Application Support/Windsurf/User/snippets Library/Application Support/Windsurf/User/keybindings.json Library/Application Support/Windsurf/User/settings.json [xdg_configuration_files] Windsurf/User/snippets Windsurf/User/keybindings.json Windsurf/User/settings.json ================================================ FILE: src/mackup/applications/wireshark.cfg ================================================ [application] name = Wireshark 2 [xdg_configuration_files] wireshark ================================================ FILE: src/mackup/applications/witch.cfg ================================================ [application] name = Witch [configuration_files] Library/Preferences/com.manytricks.Witch.plist ================================================ FILE: src/mackup/applications/wordgrinder.cfg ================================================ [application] name = wordgrinder [configuration_files] .wordgrinder.settings ================================================ FILE: src/mackup/applications/workrave.cfg ================================================ [application] name = Workrave [configuration_files] .workrave/id .workrave/historystats ================================================ FILE: src/mackup/applications/wp-cli.cfg ================================================ [application] name = WP-CLI [configuration_files] .wp-cli ================================================ FILE: src/mackup/applications/x11.cfg ================================================ [application] name = X11 [configuration_files] .Xmodmap .Xresources .fonts .xinitrc ================================================ FILE: src/mackup/applications/xamarinstudio-5.cfg ================================================ [application] name = Xamarin Studio 5 [configuration_files] Library/Preferences/XamarinStudio-5.0 ================================================ FILE: src/mackup/applications/xbar.cfg ================================================ [application] name = xbar [configuration_files] Library/Application Support/xbar/xbar.config.json Library/Application Support/xbar/plugins ================================================ FILE: src/mackup/applications/xbindkeys.cfg ================================================ [application] name = xbindkeys [configuration_files] .xbindkeysrc ================================================ FILE: src/mackup/applications/xchat.cfg ================================================ [application] name = XChat [configuration_files] .xchat2 ================================================ FILE: src/mackup/applications/xcode.cfg ================================================ [application] name = Xcode [configuration_files] Library/Preferences/com.apple.dt.Xcode.plist Library/Application Support/Developer/Shared/Xcode/Plug-ins Library/Developer/Xcode/UserData/CodeSnippets Library/Developer/Xcode/UserData/FontAndColorThemes Library/Developer/Xcode/UserData/KeyBindings Library/Developer/Xcode/UserData/Debugger Library/Developer/Xcode/UserData/xcdebugger Library/Developer/Xcode/UserData/SearchScopes.xcsclist Library/Developer/Xcode/Templates ================================================ FILE: src/mackup/applications/xee.cfg ================================================ [application] name = Xee [configuration_files] Library/Preferences/cx.c3.Xee3.plist ================================================ FILE: src/mackup/applications/xemacs.cfg ================================================ [application] name = XEmacs [configuration_files] .xemacs ================================================ FILE: src/mackup/applications/xld.cfg ================================================ [application] name = XLD [configuration_files] Library/Application Support/XLD Library/Preferences/jp.tmkk.XLD.plist ================================================ FILE: src/mackup/applications/xonsh.cfg ================================================ [application] name = Xonsh [configuration_files] .xonshrc [xdg_configuration_files] xonsh ================================================ FILE: src/mackup/applications/xtrafinder.cfg ================================================ [application] name = XtraFinder [configuration_files] Library/Preferences/com.trankynam.XtraFinder.plist ================================================ FILE: src/mackup/applications/yabai.cfg ================================================ [application] name = yabai [configuration_files] .yabairc [xdg_configuration_files] yabai/yabairc ================================================ FILE: src/mackup/applications/yarn.cfg ================================================ [application] name = yarn [configuration_files] .yarnrc ================================================ FILE: src/mackup/applications/yazi.cfg ================================================ [application] name = yazi [xdg_configuration_files] yazi/yazi.toml yazi/keymap.toml yazi/theme.toml ================================================ FILE: src/mackup/applications/youtube-dl.cfg ================================================ [application] name = youtube-dl [xdg_configuration_files] youtube-dl/config ================================================ FILE: src/mackup/applications/yummyftp.cfg ================================================ [application] name = Yummy FTP [configuration_files] Library/Preferences/com.yummysoftware.yummy-ftp.plist Library/Application Support/Yummy FTP/DefaultBookmark.bkmk Library/Application Support/Yummy FTP/Bookmarks ================================================ FILE: src/mackup/applications/zabbix-cli.cfg ================================================ [application] name = Zabbix CLI [configuration_files] .zabbix-cli/zabbix-cli.conf ================================================ FILE: src/mackup/applications/zathura.cfg ================================================ [application] name = zathura [xdg_configuration_files] zathura/zathurarc ================================================ FILE: src/mackup/applications/zed.cfg ================================================ [application] name = Zed [xdg_configuration_files] zed/keymap.json zed/settings.json ================================================ FILE: src/mackup/applications/zoom.cfg ================================================ [application] name = Zoom [configuration_files] Library/Preferences/us.zoom.Transcode.plist Library/Preferences/us.zoom.xos.Hotkey.plist Library/Preferences/us.zoom.xos.plist Library/Preferences/us.zoom.ZoomAutoUpdater.plist Library/Preferences/ZoomChat.plist ================================================ FILE: src/mackup/applications/zoxide.cfg ================================================ [application] name = zoxide [configuration_files] Library/Application Support/zoxide ================================================ FILE: src/mackup/applications/zsh.cfg ================================================ [application] name = Zsh [configuration_files] .zshenv .zprofile .zshrc .zlogin .zlogout ================================================ FILE: src/mackup/appsdb.py ================================================ """ The applications database. The Applications Database provides an easy to use interface to load application data from the Mackup Database (files). """ import configparser import os from typing import Union from .constants import APPS_DIR, CUSTOM_APPS_DIR, CUSTOM_APPS_DIR_XDG class ApplicationsDatabase: """Database containing all the configured applications.""" def __init__(self) -> None: """Create a ApplicationsDatabase instance.""" # Build the dict that will contain the properties of each application self.apps: dict[str, dict[str, Union[str, set[str]]]] = {} for config_file in ApplicationsDatabase.get_config_files(): config: configparser.ConfigParser = configparser.ConfigParser( allow_no_value=True, ) # Needed to not lowercase the configuration_files in the ini files config.optionxform = str # type: ignore if config.read(config_file): # Get the filename without the directory name filename: str = os.path.basename(config_file) # The app name is the cfg filename with the extension app_name: str = filename[: -len(".cfg")] # Start building a dict for this app self.apps[app_name] = {} # Add the fancy name for the app, for display purpose app_pretty_name: str = config.get("application", "name") self.apps[app_name]["name"] = app_pretty_name # Add the configuration files to sync config_files: set[str] = set() self.apps[app_name]["configuration_files"] = config_files if config.has_section("configuration_files"): for path in config.options("configuration_files"): if path.startswith("/"): raise ValueError( f"Unsupported absolute path: {path}", ) config_files.add(path) # Add the XDG configuration files to sync home: str = os.path.expanduser("~/") failobj: str = f"{home}.config" xdg_config_home: str = os.environ.get("XDG_CONFIG_HOME", failobj) if not xdg_config_home.startswith(home): raise ValueError( f"$XDG_CONFIG_HOME: {xdg_config_home} must be somewhere " f"within your home directory: {home}", ) if config.has_section("xdg_configuration_files"): for path in config.options("xdg_configuration_files"): if path.startswith("/"): raise ValueError( f"Unsupported absolute path: {path}", ) xdg_path = os.path.join(xdg_config_home, path) xdg_path = xdg_path.replace(home, "") config_files.add(xdg_path) @staticmethod def get_config_files() -> set[str]: """ Return the application configuration files. Return a list of configuration files describing the apps supported by Mackup. The files returned are absolute full path to those files. e.g. /usr/lib/mackup/applications/bash.cfg Only one config file per application should be returned, custom config having a priority over stock config. Legacy custom apps directory (~/.mackup/) takes priority over XDG location. Returns: set of strings. """ # Configure the config parser apps_dir: str = os.path.join( os.path.dirname(os.path.realpath(__file__)), APPS_DIR, ) # Legacy custom apps directory: ~/.mackup/ legacy_custom_apps_dir: str = os.path.join(os.environ["HOME"], CUSTOM_APPS_DIR) # XDG custom apps directory: $XDG_CONFIG_HOME/mackup/applications/ xdg_config_home: str = os.environ.get( "XDG_CONFIG_HOME", os.path.join(os.environ["HOME"], ".config"), ) xdg_custom_apps_dir: str = os.path.join(xdg_config_home, CUSTOM_APPS_DIR_XDG) # List of stock application config files config_files: set[str] = set() # Temp list of user added app config file names custom_files: set[str] = set() # Get the list of custom application config files from legacy directory first # (legacy takes priority over XDG) if os.path.isdir(legacy_custom_apps_dir): for filename in os.listdir(legacy_custom_apps_dir): if filename.endswith(".cfg"): config_files.add(os.path.join(legacy_custom_apps_dir, filename)) custom_files.add(filename) # Get custom application config files from XDG directory # (only if not already in legacy directory) if os.path.isdir(xdg_custom_apps_dir): for filename in os.listdir(xdg_custom_apps_dir): if filename.endswith(".cfg") and filename not in custom_files: config_files.add(os.path.join(xdg_custom_apps_dir, filename)) custom_files.add(filename) # Add the default provided app config files, but only if those are not # customized, as we don't want to overwrite custom app config. for filename in os.listdir(apps_dir): if filename.endswith(".cfg") and filename not in custom_files: config_files.add(os.path.join(apps_dir, filename)) return config_files def get_name(self, name: str) -> str: """ Return the fancy name of an application. Args: name (str) Returns: str """ value = self.apps[name]["name"] assert isinstance(value, str) return value def get_files(self, name: str) -> set[str]: """ Return the list of config files of an application. Args: name (str) Returns: set of str. """ value = self.apps[name]["configuration_files"] assert isinstance(value, set) return value def get_app_names(self) -> set[str]: """ Return application names. Return the list of application names that are available in the database. Returns: set of str. """ app_names: set[str] = set() for name in self.apps: app_names.add(name) return app_names def get_pretty_app_names(self) -> set[str]: """ Return the list of pretty app names that are available in the database. Returns: set of str. """ pretty_app_names: set[str] = set() for app_name in self.get_app_names(): pretty_app_names.add(self.get_name(app_name)) return pretty_app_names ================================================ FILE: src/mackup/config.py ================================================ """Package used to manage the .mackup.cfg config file.""" import configparser import os import os.path from pathlib import Path from typing import Optional from .constants import ( CUSTOM_APPS_DIR, CUSTOM_APPS_DIR_XDG, ENGINE_DROPBOX, ENGINE_FS, ENGINE_GDRIVE, ENGINE_ICLOUD, MACKUP_BACKUP_PATH, MACKUP_CONFIG_FILE, ) from .utils import ( error, get_dropbox_folder_location, get_google_drive_folder_location, get_icloud_folder_location, ) class Config: """The Mackup Config class.""" def __init__(self, filename: Optional[str] = None) -> None: """ Create a Config instance. Args: filename (str): Optional filename of the config file. If empty, defaults to MACKUP_CONFIG_FILE """ assert isinstance(filename, str) or filename is None # Initialize the parser self._parser = self._setup_parser(filename) # Do we have an old config file? self._warn_on_old_config() # Get the storage engine self._engine = self._parse_engine() # Get the path where the Mackup folder is self._path = self._parse_path() # Get the directory replacing 'Mackup', if any self._directory = self._parse_directory() # Get the list of apps to ignore self._apps_to_ignore = self._parse_apps_to_ignore() # Get the list of apps to allow self._apps_to_sync = self._parse_apps_to_sync() @property def engine(self) -> str: """ The engine used by the storage. ENGINE_DROPBOX, ENGINE_GDRIVE, ENGINE_ICLOUD or ENGINE_FS. Returns: str """ return str(self._engine) @property def path(self) -> str: """ Path to the Mackup configuration files. The path to the directory where Mackup is gonna create and store his directory. Returns: str """ return str(self._path) @property def directory(self) -> str: """ The name of the Mackup directory, named Mackup by default. Returns: str """ return str(self._directory) @property def fullpath(self) -> str: """ Full path to the Mackup configuration files. The full path to the directory when Mackup is storing the configuration files. Returns: str """ return str(os.path.join(self.path, self.directory)) @property def apps_to_ignore(self) -> set[str]: """ Get the list of applications ignored in the config file. Returns: set. Set of application names to ignore, lowercase """ return set(self._apps_to_ignore) @property def apps_to_sync(self) -> set[str]: """ Get the list of applications allowed in the config file. Returns: set. Set of application names to allow, lowercase """ return set(self._apps_to_sync) def _setup_parser( self, filename: Optional[str] = None, ) -> configparser.ConfigParser: """ Configure the ConfigParser instance the way we want it. Args: filename (str) or None Returns: ConfigParser """ assert isinstance(filename, str) or filename is None parser = configparser.ConfigParser( allow_no_value=True, inline_comment_prefixes=(";", "#"), ) parser.read(self._best_config_path(filename)) return parser def _best_config_path(self, filename: Optional[str] = None) -> str: """ If no filename is provided, we try to find one in according to the following order, note that we will always check the original default of `~/.mackup.cfg` first before checking the other options: - ~/.mackup.cfg - $MACKUP_CONFIG - $XDG_CONFIG_HOME/mackup/mackup.cfg - ~/.config/mackup/mackup.cfg if none of these files exist, we create ~/.mackup.cfg Args: filename (str or None, optional): Optional override for the config file path. Can be absolute or relative to home directory. Defaults to None. Returns: str: the absolute path to the config file """ assert isinstance(filename, str) or filename is None # If we are not overriding the config filename config_path: Path if not filename: default = Path.home() / MACKUP_CONFIG_FILE search_paths = [ # 1. the default config file is ~/.mackup.cfg default, # 2. check for the MACKUP_CONFIG envvar Path(os.environ.get("MACKUP_CONFIG", "")).expanduser(), # 3. check for a config file in the XDG_CONFIG_HOME directory ( Path(os.environ.get("XDG_CONFIG_HOME", "~/.config")).expanduser() / "mackup" / MACKUP_CONFIG_FILE.lstrip(".") ), ] config_path = next((p for p in search_paths if p.is_file()), default) else: # Support both absolute and relative paths config_path = Path(filename).expanduser() if not config_path.is_absolute(): config_path = Path.home() / filename # When explicitly specified, check that the file exists if not config_path.is_file(): error( f"The config file '{config_path}' does not exist. Aborting.", ) try: # Make sure the config file is in the home directory config_path.relative_to(Path.home()) except ValueError: error( f"The config file '{config_path}' is not in your home " "directory. Aborting.", ) # return the absolute path to the config file return str(config_path.absolute()) def _warn_on_old_config(self) -> None: """Warn the user if an old config format is detected.""" # Is an old section in the config file? old_sections = ["Allowed Applications", "Ignored Applications"] for old_section in old_sections: if self._parser.has_section(old_section): error( "Old config file detected. Aborting.\n" "\n" "An old section (e.g. [Allowed Applications]" " or [Ignored Applications] has been detected" f" in your {MACKUP_CONFIG_FILE} file.\n" "I'd rather do nothing than do something you" " do not want me to do.\n" "\n" "Please read the up to date documentation on" " and migrate" " your configuration file.", ) def _parse_engine(self) -> str: """ Parse the storage engine in the config. Returns: str """ if self._parser.has_option("storage", "engine"): engine = str(self._parser.get("storage", "engine")) else: engine = ENGINE_DROPBOX assert isinstance(engine, str) if engine not in [ ENGINE_DROPBOX, ENGINE_GDRIVE, ENGINE_ICLOUD, ENGINE_FS, ]: raise ConfigError(f"Unknown storage engine: {engine}") return str(engine) def _parse_path(self) -> str: """ Parse the storage path in the config. Returns: str """ if self.engine == ENGINE_DROPBOX: path = get_dropbox_folder_location() elif self.engine == ENGINE_GDRIVE: path = get_google_drive_folder_location() elif self.engine == ENGINE_ICLOUD: path = get_icloud_folder_location() elif self.engine == ENGINE_FS: if self._parser.has_option("storage", "path"): cfg_path = self._parser.get("storage", "path") path = os.path.join(os.environ["HOME"], cfg_path) else: raise ConfigError( "The required 'path' can't be found while" " the 'file_system' engine is used.", ) return str(path) def _parse_directory(self) -> str: """ Parse the storage directory in the config. Returns: str """ if self._parser.has_option("storage", "directory"): directory = self._parser.get("storage", "directory") # Don't allow CUSTOM_APPS_DIR or XDG custom apps dir as a storage directory if directory == CUSTOM_APPS_DIR: raise ConfigError( f"{CUSTOM_APPS_DIR} cannot be used as a storage directory.", ) xdg_custom_apps_dir = os.path.join(".config", CUSTOM_APPS_DIR_XDG) in_xdg_dir = directory in (CUSTOM_APPS_DIR_XDG, xdg_custom_apps_dir) if in_xdg_dir or directory.endswith("/" + xdg_custom_apps_dir): raise ConfigError( f"{CUSTOM_APPS_DIR_XDG} cannot be used as a storage directory.", ) else: directory = MACKUP_BACKUP_PATH return str(directory) def _parse_apps_to_ignore(self) -> set[str]: """ Parse the applications to ignore in the config. Returns: set """ # We ignore nothing by default apps_to_ignore = set() # Is the "[applications_to_ignore]" in the cfg file? section_title = "applications_to_ignore" if self._parser.has_section(section_title): apps_to_ignore = set(self._parser.options(section_title)) return apps_to_ignore def _parse_apps_to_sync(self) -> set[str]: """ Parse the applications to backup in the config. Returns: set """ # We allow nothing by default apps_to_sync = set() # Is the "[applications_to_sync]" section in the cfg file? section_title = "applications_to_sync" if self._parser.has_section(section_title): apps_to_sync = set(self._parser.options(section_title)) return apps_to_sync class ConfigError(Exception): """Exception used for handle errors in the configuration.""" ================================================ FILE: src/mackup/constants.py ================================================ """Constants used in Mackup.""" from importlib.metadata import PackageNotFoundError, version # Support platforms PLATFORM_DARWIN: str = "Darwin" PLATFORM_LINUX: str = "Linux" # Directory containing the application configs APPS_DIR: str = "applications" # Mackup application name MACKUP_APP_NAME: str = "mackup" # Default Mackup backup path where it stores its files in Dropbox MACKUP_BACKUP_PATH: str = "Mackup" # Mackup config file MACKUP_CONFIG_FILE: str = ".mackup.cfg" def _get_version() -> str: """Return package version, or a safe fallback when metadata is unavailable.""" try: return version(MACKUP_APP_NAME) except PackageNotFoundError: return "unknown" # Current version VERSION: str = _get_version() # Directory that can contains user defined app configs CUSTOM_APPS_DIR: str = ".mackup" # XDG-compliant directory for user defined app configs (relative to XDG_CONFIG_HOME) CUSTOM_APPS_DIR_XDG: str = "mackup/applications" # Supported engines ENGINE_DROPBOX: str = "dropbox" ENGINE_FS: str = "file_system" ENGINE_GDRIVE: str = "google_drive" ENGINE_ICLOUD: str = "icloud" DOCUMENTATION_URL: str = "https://github.com/lra/mackup/blob/master/doc/README.md" # Error message displayed when mackup can't find the storage specified # in the config (or the default one). ERROR_UNABLE_TO_FIND_STORAGE: str = ( "Unable to find your {provider} =(\n" f"If this is the first time you use {MACKUP_APP_NAME}, you may want " "to use another provider.\n" "Take a look at the documentation [1] to know more about " "how to configure mackup.\n\n" f"[1]: {DOCUMENTATION_URL}" ) ================================================ FILE: src/mackup/mackup.py ================================================ """ The Mackup Class. The Mackup class is keeping all the state that Mackup needs to keep during its runtime. It also provides easy to use interface that is used by the Mackup UI. The only UI for now is the command line. """ import os import os.path import shutil import tempfile from typing import Optional from . import appsdb, config, utils class Mackup: """Main Mackup class.""" def __init__(self, config_file: Optional[str] = None) -> None: """Mackup Constructor.""" self._config: config.Config = config.Config(config_file) self.mackup_folder: str = self._config.fullpath self.temp_folder: str = tempfile.mkdtemp(prefix="mackup_tmp_") def check_for_usable_environment(self) -> None: """Check if the current env is usable and has everything's required.""" # Allow only explicit superuser usage if os.geteuid() == 0 and not utils.CAN_RUN_AS_ROOT: utils.error( "Running Mackup as superuser can be dangerous." " Don't do it unless you know what you're doing!" " Run mackup --help for guidance.", ) # Do we have a folder set to save Mackup content into? if not os.path.isdir(self._config.path): utils.error( f"Unable to find the storage folder: {self._config.path}", ) # Is Sublime Text running? # if is_process_running('Sublime Text'): # error("Sublime Text is running. It is known to cause problems" # " when Sublime Text is running while I backup or restore" # " its configuration files. Please close Sublime Text and" # " run me again.") def check_for_usable_backup_env(self) -> None: """Check if the current env can be used to back up files.""" self.check_for_usable_environment() self.create_mackup_home() def check_for_usable_restore_env(self) -> None: """Check if the current env can be used to restore files.""" self.check_for_usable_environment() if not os.path.isdir(self.mackup_folder): utils.error( f"Unable to find the Mackup folder: {self.mackup_folder}\n" "You might want to back up some files or get your" " storage directory synced first.", ) def clean_temp_folder(self) -> None: """Delete the temp folder and files created while running.""" shutil.rmtree(self.temp_folder) def create_mackup_home(self) -> None: """If the Mackup home folder does not exist, create it.""" if not os.path.isdir(self.mackup_folder): if utils.confirm( "Mackup needs a directory to store your" " configuration files\n" f"Do you want to create it now? <{self.mackup_folder}>", ): os.makedirs(self.mackup_folder) else: utils.error("Mackup can't do anything without a home =(") def get_apps_to_backup(self) -> set[str]: """ Get the list of applications that should be backed up by Mackup. It's the list of allowed apps minus the list of ignored apps. Returns: (set) List of application names to back up """ # Instantiate the app db app_db: appsdb.ApplicationsDatabase = appsdb.ApplicationsDatabase() # If a list of apps to sync is specify, we only allow those # Or we allow every supported app by default apps_to_backup: set[str] = self._config.apps_to_sync or app_db.get_app_names() # Remove the specified apps to ignore for app_name in self._config.apps_to_ignore: apps_to_backup.discard(app_name) return apps_to_backup ================================================ FILE: src/mackup/main.py ================================================ """Mackup. Keep your application settings in sync. Copyright (C) 2013-2025 Laurent Raufaste Usage: mackup [options] list mackup [options] show mackup [options] backup mackup [options] restore mackup [options] link install mackup [options] link mackup [options] link uninstall mackup (-h | --help) Options: -h --help Show this screen. -f --force Force every question asked to be answered with "Yes". --force-no Force every question asked to be answered with "No". -r --root Allow mackup to be run as superuser. -n --dry-run Show steps without executing. -v --verbose Show additional details. -c --config-file= Specify custom config file path. --version Show version. Modes of action: - mackup list: display a list of all supported applications. - mackup show: display the details for a supported application. - mackup backup: copy local config files in the configured remote folder. - mackup restore: copy config files from the configured remote folder locally. - mackup link install: moves local config files in remote folder, and links. - mackup link: links local config files from the remote folder. - mackup link uninstall: removes the links and copy config files locally. By default, Mackup syncs all application data via Dropbox, but may be configured to exclude applications or use a different backend with a .mackup.cfg file. See https://github.com/lra/mackup/tree/master/doc for more information. """ import sys from typing import Any, Optional from docopt import docopt from . import utils from .application import ApplicationProfile from .appsdb import ApplicationsDatabase from .constants import MACKUP_APP_NAME, VERSION from .mackup import Mackup class ColorFormatCodes: BLUE = "\033[34m" BOLD = "\033[1m" NORMAL = "\033[0m" def header(text: str) -> str: return ColorFormatCodes.BLUE + text + ColorFormatCodes.NORMAL def bold(text: str) -> str: return ColorFormatCodes.BOLD + text + ColorFormatCodes.NORMAL def main() -> None: """Main function.""" # Get the command line arg docstring = __doc__ if not docstring: sys.exit( "Usage information is not available because __doc__ is None. " "This can happen when running Python with optimizations (python -OO). " "Please run Mackup without -OO to use the command-line interface.", ) assert docstring is not None # for type narrowing after sys.exit args: dict[str, Any] = docopt(docstring, version=f"Mackup {VERSION}") if args["--force"] and args["--force-no"]: sys.exit("Options --force and --force-no are mutually exclusive.") config_file: Optional[str] = args.get("--config-file") mckp: Mackup = Mackup(config_file) app_db: ApplicationsDatabase = ApplicationsDatabase() def print_app_header(app_name: str) -> None: if verbose: header_str = header("---") print(f"\n{header_str} {bold(app_name)} {header_str}") # If we want to answer mackup with "yes" for each question if args["--force"]: utils.FORCE_YES = True # If we want to answer mackup with "no" for each question if args["--force-no"]: utils.FORCE_NO = True # Allow mackup to be run as root if args["--root"]: utils.CAN_RUN_AS_ROOT = True dry_run: bool = args["--dry-run"] verbose: bool = args["--verbose"] # mackup list if args["list"]: # Display the list of supported applications mckp.check_for_usable_environment() output: str = "Supported applications:\n" for app_name in sorted(app_db.get_app_names()): output += f" - {app_name}\n" output += "\n" output += ( f"{len(app_db.get_app_names())} applications supported in " f"Mackup v{VERSION}" ) print(output) # mackup show elif args["show"]: mckp.check_for_usable_environment() requested_app_name: str = args[""] # Make sure the app exists if requested_app_name not in app_db.get_app_names(): sys.exit(f"Unsupported application: {requested_app_name}") print(f"Name: {app_db.get_name(requested_app_name)}") print("Configuration files:") for file in app_db.get_files(requested_app_name): print(f" - {file}") # mackup backup elif args["backup"]: mckp.check_for_usable_backup_env() # Create a backup of the files of each application for app_name in sorted(mckp.get_apps_to_backup()): app: ApplicationProfile = ApplicationProfile( mckp, app_db.get_files(app_name), dry_run, verbose, ) print_app_header(app_name) app.copy_files_to_mackup_folder() # mackup restore elif args["restore"]: mckp.check_for_usable_restore_env() # Recover a backup of the files of each application for app_name in sorted(mckp.get_apps_to_backup()): app = ApplicationProfile(mckp, app_db.get_files(app_name), dry_run, verbose) print_app_header(app_name) app.copy_files_from_mackup_folder() # mackup link install elif args["link"] and args["install"]: # Check the env where the command is being run mckp.check_for_usable_backup_env() # Create a link for each application for app_name in sorted(mckp.get_apps_to_backup()): app = ApplicationProfile(mckp, app_db.get_files(app_name), dry_run, verbose) print_app_header(app_name) app.link_install() # mackup link uninstall elif args["link"] and args["uninstall"]: # Check the env where the command is being run mckp.check_for_usable_restore_env() if dry_run or ( utils.confirm( "You are going to uninstall Mackup.\n" "Every configuration file, setting and dotfile" " managed by Mackup will be unlinked and copied back" " to their original place, in your home folder.\n" "Are you sure?", ) ): # Uninstall the apps except Mackup, which we'll uninstall last, to # keep the settings as long as possible app_names = mckp.get_apps_to_backup() app_names.discard(MACKUP_APP_NAME) for app_name in sorted(app_names): app = ApplicationProfile( mckp, app_db.get_files(app_name), dry_run, verbose, ) print_app_header(app_name) app.link_uninstall() # Restore the Mackup config before any other config, as we might # need it to know about custom settings mackup_app = ApplicationProfile( mckp, app_db.get_files(MACKUP_APP_NAME), dry_run, verbose, ) mackup_app.link_uninstall() # Delete the Mackup folder in Dropbox # Don't delete this as there might be other Macs that aren't # uninstalled yet # delete(mckp.mackup_folder) print( "\n" "All your files have been put back into place. You can now" " safely uninstall Mackup.\n" "\n" "Thanks for using Mackup!", ) # mackup link elif args["link"]: # Check the env where the command is being run mckp.check_for_usable_restore_env() # Restore the Mackup config before any other config, as we might need # it to know about custom settings mackup_app = ApplicationProfile( mckp, app_db.get_files(MACKUP_APP_NAME), dry_run, verbose, ) print_app_header(MACKUP_APP_NAME) mackup_app.link() # Initialize again the apps db, as the Mackup config might have changed # it mckp = Mackup(config_file) app_db = ApplicationsDatabase() # Restore the rest of the app configs, using the restored Mackup config app_names = mckp.get_apps_to_backup() # Mackup has already been done app_names.discard(MACKUP_APP_NAME) for app_name in sorted(app_names): app = ApplicationProfile(mckp, app_db.get_files(app_name), dry_run, verbose) print_app_header(app_name) app.link() # Delete the tmp folder mckp.clean_temp_folder() ================================================ FILE: src/mackup/utils.py ================================================ """System static utilities being used by the modules.""" import base64 import binascii import os import platform import shutil import sqlite3 import stat import subprocess import sys from typing import NoReturn, Optional from . import constants # Flag that controls how user confirmation works. # If True, the user wants to say "yes" to everything. FORCE_YES: bool = False # If True, the user wants to say "no" to everything. FORCE_NO: bool = False # Flag that control if mackup can be run as root CAN_RUN_AS_ROOT: bool = False def confirm(question: str) -> bool: """ Ask the user if he really wants something to happen. Args: question(str): What can happen Returns: (boolean): Confirmed or not """ if FORCE_YES: return True if FORCE_NO: return False while True: answer: str = input(question + " ").lower() if answer in {"yes", "y"}: confirmed: bool = True break if answer in {"no", "n"}: confirmed = False break return confirmed def delete(filepath: str) -> None: """ Delete the given file, directory or link. It Should support undelete later on. Args: filepath (str): Absolute full path to a file. e.g. /path/to/file """ # Some files have ACLs, let's remove them recursively remove_acl(filepath) # Some files have immutable attributes, let's remove them recursively remove_immutable_attribute(filepath) # Finally remove the files and folders if os.path.isfile(filepath) or os.path.islink(filepath): os.remove(filepath) elif os.path.isdir(filepath): shutil.rmtree(filepath) def copy(src: str, dst: str) -> None: """ Copy a file or a folder (recursively) from src to dst. For the sake of simplicity, both src and dst must be absolute path and must include the filename of the file or folder. Also do not include any trailing slash. e.g. copy('/path/to/src_file', '/path/to/dst_file') or copy('/path/to/src_folder', '/path/to/dst_folder') But not: copy('/path/to/src_file', 'path/to/') or copy('/path/to/src_folder/', '/path/to/dst_folder') Args: src (str): Source file or folder dst (str): Destination file or folder """ assert isinstance(src, str) assert os.path.exists(src) assert isinstance(dst, str) # Create the path to the dst file if it does not exist abs_path = os.path.dirname(os.path.abspath(dst)) if not os.path.isdir(abs_path): os.makedirs(abs_path) # We need to copy a single file if os.path.isfile(src): # Copy the src file to dst shutil.copy(src, dst) # We need to copy a whole folder elif os.path.isdir(src): shutil.copytree(src, dst, dirs_exist_ok=True, copy_function=shutil.copy) # What the heck is this? else: raise ValueError(f"Unsupported file: {src}") # Set the good mode to the file or folder recursively chmod(dst) def link(target: str, link_to: str) -> None: """ Create a link to a target file or a folder. For the sake of simplicity, both target and link_to must be absolute path and must include the filename of the file or folder. Also do not include any trailing slash. e.g. link('/path/to/file', '/path/to/link') But not: link('/path/to/file', 'path/to/') or link('/path/to/folder/', '/path/to/link') Args: target (str): file or folder the link will point to link_to (str): Link to create """ assert isinstance(target, str) assert os.path.exists(target) assert isinstance(link_to, str) # Create the path to the link if it does not exist abs_path = os.path.dirname(os.path.abspath(link_to)) if not os.path.isdir(abs_path): os.makedirs(abs_path) # Make sure the file or folder recursively has the good mode chmod(target) # Create the link to target os.symlink(target, link_to) def chmod(target: str) -> None: """ Recursively set the chmod for files to 0600 and 0700 for folders. It's ok unless we need something more specific. Args: target (str): Root file or folder """ assert isinstance(target, str) assert os.path.exists(target) file_mode = stat.S_IRUSR | stat.S_IWUSR folder_mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR # Remove the immutable attribute recursively if there is one remove_immutable_attribute(target) if os.path.isfile(target): os.chmod(target, file_mode) elif os.path.isdir(target): # chmod the root item os.chmod(target, folder_mode) # chmod recursively in the folder it it's one for root, dirs, files in os.walk(target): for cur_dir in dirs: os.chmod(os.path.join(root, cur_dir), folder_mode) for cur_file in files: os.chmod(os.path.join(root, cur_file), file_mode) else: raise ValueError(f"Unsupported file type: {target}") def error(message: str) -> NoReturn: """ Throw an error with the given message and immediately quit. Args: message(str): The message to display. """ fail: str = "\033[91m" end: str = "\033[0m" sys.exit(fail + f"Error: {message}" + end) def get_dropbox_folder_location() -> str: """ Try to locate the Dropbox folder. Returns: (str) Full path to the current Dropbox folder """ host_db_path = os.path.join(os.environ["HOME"], ".dropbox/host.db") min_host_db_fields = 2 try: with open(host_db_path) as f_hostdb: data = f_hostdb.read().split() if len(data) < min_host_db_fields: raise ValueError("Malformed Dropbox host.db") dropbox_home = base64.b64decode(data[1], validate=True).decode() except ( OSError, ValueError, binascii.Error, UnicodeEncodeError, UnicodeDecodeError, ): error(constants.ERROR_UNABLE_TO_FIND_STORAGE.format(provider="Dropbox install")) return dropbox_home def get_google_drive_folder_location() -> str: """ Try to locate the Google Drive folder. Returns: (str) Full path to the current Google Drive folder """ gdrive_db_path = "Library/Application Support/Google/Drive/sync_config.db" yosemite_gdrive_db_path = ( "Library/Application Support/Google/Drive/user_default/sync_config.db" ) yosemite_gdrive_db = os.path.join(os.environ["HOME"], yosemite_gdrive_db_path) if os.path.isfile(yosemite_gdrive_db): gdrive_db_path = yosemite_gdrive_db googledrive_home: Optional[str] = None gdrive_db = ( gdrive_db_path if os.path.isabs(gdrive_db_path) else os.path.join(os.environ["HOME"], gdrive_db_path) ) if os.path.isfile(gdrive_db): try: with sqlite3.connect(gdrive_db) as con: cur = con.cursor() query = ( "SELECT data_value " "FROM data " "WHERE entry_key = 'local_sync_root_path';" ) cur.execute(query) data = cur.fetchone() if data and data[0]: googledrive_home = str(data[0]) except sqlite3.Error: googledrive_home = None if googledrive_home: return googledrive_home error( constants.ERROR_UNABLE_TO_FIND_STORAGE.format( provider="Google Drive install", ), ) def get_icloud_folder_location() -> str: """ Try to locate the iCloud Drive folder. Returns: (str) Full path to the iCloud Drive folder. """ yosemite_icloud_path = "~/Library/Mobile Documents/com~apple~CloudDocs/" icloud_home = os.path.expanduser(yosemite_icloud_path) if not os.path.isdir(icloud_home): error(constants.ERROR_UNABLE_TO_FIND_STORAGE.format(provider="iCloud Drive")) return str(icloud_home) def is_process_running(process_name: str) -> bool: """ Check if a process with the given name is running. Args: (str): Process name, e.g. "Sublime Text" Returns: (bool): True if the process is running """ is_running: bool = False # On systems with pgrep, check if the given process is running if os.path.isfile("/usr/bin/pgrep"): with open(os.devnull, "wb") as dev_null: returncode: int = subprocess.call( ["/usr/bin/pgrep", process_name], stdout=dev_null, ) is_running = bool(returncode == 0) return is_running def remove_acl(path: str) -> None: """ Remove the ACL of the file or folder located on the given path. Also remove the ACL of any file and folder below the given one, recursively. Args: path (str): Path to the file or folder to remove the ACL for, recursively. """ # Some files have ACLs, let's remove them recursively if platform.system() == constants.PLATFORM_DARWIN and os.path.isfile("/bin/chmod"): subprocess.call(["/bin/chmod", "-R", "-N", path]) elif (platform.system() == constants.PLATFORM_LINUX) and os.path.isfile( "/bin/setfacl", ): subprocess.call(["/bin/setfacl", "-R", "-b", path]) def remove_immutable_attribute(path: str) -> None: """ Remove the immutable attribute of the given path. Remove the immutable attribute of the file or folder located on the given path. Also remove the immutable attribute of any file and folder below the given one, recursively. Args: path (str): Path to the file or folder to remove the immutable attribute for, recursively. """ # Some files have ACLs, let's remove them recursively if (platform.system() == constants.PLATFORM_DARWIN) and os.path.isfile( "/usr/bin/chflags", ): subprocess.call(["/usr/bin/chflags", "-R", "nouchg", path]) elif platform.system() == constants.PLATFORM_LINUX and os.path.isfile( "/usr/bin/chattr", ): subprocess.call(["/usr/bin/chattr", "-R", "-f", "-i", path]) def can_file_be_synced_on_current_platform(path: str) -> bool: """ Check if the given path can be synced locally. Check if it makes sense to sync the file at the given path on the current platform. For now we don't sync any file in the ~/Library folder on GNU/Linux. There might be other exceptions in the future. Args: (str): Path to the file or folder to check. If relative, prepend it with the home folder. 'abc' becomes '~/abc' '/def' stays '/def' Returns: (bool): True if given file can be synced """ can_be_synced: bool = True # If the given path is relative, prepend home fullpath: str = os.path.join(os.environ["HOME"], path) # Compute the ~/Library path on macOS # End it with a slash because we are looking for this specific folder and # not any file/folder named LibrarySomething library_path: str = os.path.join(os.environ["HOME"], "Library/") is_linux = platform.system() == constants.PLATFORM_LINUX if is_linux and fullpath.startswith(library_path): can_be_synced = False return can_be_synced ================================================ FILE: tests/README.md ================================================ # Tests Tests are put in this folder. Feel free to add more, the more the better! ## How to run the tests ```bash brew install uv make test ``` And you should see ```text . ---------------------------------------------------------------------- Ran 1 test in 0.016s OK ``` Yeah, I wrote this file when there was only 1 test, I hope there will be more when you read it! ================================================ FILE: tests/__init__.py ================================================ """Test package for Mackup.""" ================================================ FILE: tests/fixtures/.mackup/legacy-test-app.cfg ================================================ [application] name = Legacy Test App [configuration_files] .legacy-test-app-config ================================================ FILE: tests/fixtures/.mackup/priority-test-app.cfg ================================================ [application] name = Priority Test App Legacy [configuration_files] .priority-test-legacy ================================================ FILE: tests/fixtures/Library/Application Support/Box/Box Sync/sync_root_folder.txt ================================================ /Users/whatever/Box Sync ================================================ FILE: tests/fixtures/Library/Mobile Documents/com~apple~CloudDocs/_blank_.md ================================================ Blank file for git sync ================================================ FILE: tests/fixtures/mackup-apps_to_ignore.cfg ================================================ [applications_to_ignore] subversion ; inline comment sequel-pro # inline comment sabnzbd ================================================ FILE: tests/fixtures/mackup-apps_to_ignore_and_sync.cfg ================================================ [applications_to_sync] sabnzbd sublime-text-3 x11 vim [applications_to_ignore] subversion sequel-pro sabnzbd ================================================ FILE: tests/fixtures/mackup-apps_to_sync.cfg ================================================ [applications_to_sync] sabnzbd sublime-text-3 x11 ================================================ FILE: tests/fixtures/mackup-empty.cfg ================================================ ================================================ FILE: tests/fixtures/mackup-engine-dropbox.cfg ================================================ [storage] engine = dropbox directory = some_weirld_name ================================================ FILE: tests/fixtures/mackup-engine-file_system-absolute.cfg ================================================ [storage] engine = file_system path = /some/absolute/folder directory = custom_folder [applications_to_ignore] subversion sequel-pro ================================================ FILE: tests/fixtures/mackup-engine-file_system-no_path.cfg ================================================ [storage] engine = file_system directory = Mackup ================================================ FILE: tests/fixtures/mackup-engine-file_system.cfg ================================================ [storage] engine = file_system path = some/relative/folder [applications_to_sync] sabnzbd sublime-text-3 x11 ================================================ FILE: tests/fixtures/mackup-engine-google_drive.cfg ================================================ [storage] engine = google_drive [applications_to_ignore] subversion sequel-pro sabnzbd [applications_to_sync] sabnzbd sublime-text-3 x11 ================================================ FILE: tests/fixtures/mackup-engine-icloud.cfg ================================================ [storage] engine = icloud [applications_to_ignore] subversion sequel-pro sabnzbd [applications_to_sync] sabnzbd sublime-text-3 x11 ================================================ FILE: tests/fixtures/mackup-engine-unknown.cfg ================================================ [storage] engine = unknown_engine ================================================ FILE: tests/fixtures/mackup-envarcheck.cfg ================================================ [test] testtype = test_config_envvar ================================================ FILE: tests/fixtures/mackup-old-config.cfg ================================================ [storage] engine = file_system path = /some/absolute/folder directory = custom_folder [applications_to_ignore] subversion sequel-pro [Allowed Applications] ssh git [Ignored Applications] vim ================================================ FILE: tests/fixtures/xdg-config-home/mackup/applications/priority-test-app.cfg ================================================ [application] name = Priority Test App XDG [configuration_files] .priority-test-xdg ================================================ FILE: tests/fixtures/xdg-config-home/mackup/applications/xdg-test-app.cfg ================================================ [application] name = XDG Test App [configuration_files] .xdg-test-app-config ================================================ FILE: tests/fixtures/xdg-config-home/mackup/mackup.cfg ================================================ [test] testtype = test_config_xdg ================================================ FILE: tests/test_application.py ================================================ import os import shutil import sys import tempfile import unittest from io import StringIO from unittest.mock import Mock, patch from mackup.application import ApplicationProfile from mackup.mackup import Mackup class TestApplicationProfile(unittest.TestCase): def setUp(self): """Set up test fixtures.""" # Create a mock Mackup instance self.mock_mackup = Mock(spec=Mackup) self.mock_mackup.mackup_folder = tempfile.mkdtemp() # Create a temporary home directory self.temp_home = tempfile.mkdtemp() # Save original HOME and set it to temp directory self.original_home = os.environ.get("HOME") os.environ["HOME"] = self.temp_home # Define test files self.test_files = {".testfile", ".testfolder"} # Create the ApplicationProfile instance self.app_profile = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=False, verbose=False, ) def tearDown(self): """Clean up test fixtures.""" # Restore original HOME if self.original_home: os.environ["HOME"] = self.original_home else: del os.environ["HOME"] # Clean up temporary directories if os.path.exists(self.temp_home): shutil.rmtree(self.temp_home) if os.path.exists(self.mock_mackup.mackup_folder): shutil.rmtree(self.mock_mackup.mackup_folder) def test_copy_files_to_mackup_folder_permission_error(self): """Test PermissionError handling in copy_files_to_mackup_folder.""" # Create a test file in the home directory test_file = ".testfile" home_filepath = os.path.join(self.temp_home, test_file) # Create the actual file with open(home_filepath, "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the error message was printed output = captured_output.getvalue() assert "Error: Unable to copy file" in output assert "permission issue" in output assert home_filepath in output def test_files_are_sorted_for_deterministic_processing(self): """Application files should always be processed in sorted order.""" unsorted_files = {"z-last", "a-first", "m-middle"} app_profile = ApplicationProfile( mackup=self.mock_mackup, files=unsorted_files, dry_run=False, verbose=False, ) assert app_profile.files == ["a-first", "m-middle", "z-last"] def test_copy_files_to_mackup_folder_permission_error_verbose(self): """Test PermissionError handling in copy_files_to_mackup_folder verbose.""" # Create a verbose ApplicationProfile app_profile_verbose = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=False, verbose=True, ) # Create a test file in the home directory test_file = ".testfile" home_filepath = os.path.join(self.temp_home, test_file) # Create the actual file with open(home_filepath, "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method app_profile_verbose.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the verbose backing up message and error message were printed output = captured_output.getvalue() assert "Backing up" in output assert "Error: Unable to copy file" in output assert "permission issue" in output def test_copy_files_from_mackup_folder_permission_error(self): """Test PermissionError handling in copy_files_from_mackup_folder.""" # Create a test file in the mackup directory test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) # Create the actual file with open(mackup_filepath, "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_from_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the error message was printed output = captured_output.getvalue() assert "Error: Unable to copy file" in output assert "permission issue" in output assert mackup_filepath in output def test_copy_files_from_mackup_folder_permission_error_verbose(self): """Test PermissionError handling in copy_files_from_mackup_folder verbose.""" # Create a verbose ApplicationProfile app_profile_verbose = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=False, verbose=True, ) # Create a test file in the mackup directory test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) # Create the actual file with open(mackup_filepath, "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method app_profile_verbose.copy_files_from_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the verbose recovering message and error message were printed output = captured_output.getvalue() assert "Recovering" in output assert "Error: Unable to copy file" in output assert "permission issue" in output def test_copy_files_to_mackup_folder_with_directory_permission_error(self): """Test PermissionError with a directory in copy_files_to_mackup_folder.""" # Create a test directory in the home directory test_dir = ".testfolder" home_dirpath = os.path.join(self.temp_home, test_dir) os.makedirs(home_dirpath) # Create a file inside the directory with open(os.path.join(home_dirpath, "testfile.txt"), "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied for directory") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the error message was printed output = captured_output.getvalue() assert "Error: Unable to copy file" in output assert "permission issue" in output assert home_dirpath in output def test_copy_files_from_mackup_folder_with_directory_permission_error(self): """Test PermissionError with a directory in copy_files_from_mackup_folder.""" # Create a test directory in the mackup directory test_dir = ".testfolder" mackup_dirpath = os.path.join(self.mock_mackup.mackup_folder, test_dir) os.makedirs(mackup_dirpath) # Create a file inside the directory with open(os.path.join(mackup_dirpath, "testfile.txt"), "w") as f: f.write("test content") # Patch utils.copy to raise PermissionError with patch("mackup.application.utils.copy") as mock_copy: mock_copy.side_effect = PermissionError("Permission denied for directory") # Capture stdout to verify the error message captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_from_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was called mock_copy.assert_called_once() # Verify that the error message was printed output = captured_output.getvalue() assert "Error: Unable to copy file" in output assert "permission issue" in output assert mackup_dirpath in output def test_copy_files_to_mackup_folder_dry_run_no_permission_error(self): """Test dry_run mode doesn't trigger PermissionError in backup.""" # Create a dry_run ApplicationProfile app_profile_dry = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=True, verbose=False, ) # Create a test file in the home directory test_file = ".testfile" home_filepath = os.path.join(self.temp_home, test_file) # Create the actual file with open(home_filepath, "w") as f: f.write("test content") # Patch utils.copy - it should NOT be called in dry_run mode with patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method app_profile_dry.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was NOT called (dry_run mode) mock_copy.assert_not_called() # Verify that the backing up message was printed output = captured_output.getvalue() assert "Backing up" in output def test_copy_files_from_mackup_folder_dry_run_no_permission_error(self): """Test dry_run mode doesn't trigger PermissionError in restore.""" # Create a dry_run ApplicationProfile app_profile_dry = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=True, verbose=False, ) # Create a test file in the mackup directory test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) # Create the actual file with open(mackup_filepath, "w") as f: f.write("test content") # Patch utils.copy - it should NOT be called in dry_run mode with patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method app_profile_dry.copy_files_from_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy was NOT called (dry_run mode) mock_copy.assert_not_called() # Verify that the recovering message was printed output = captured_output.getvalue() assert "Recovering" in output def test_copy_files_to_mackup_folder_decline_replace_skips_copy(self): """Test backup does not overwrite when user declines replacement.""" test_file = ".testfile" home_filepath = os.path.join(self.temp_home, test_file) mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) with open(home_filepath, "w") as f: f.write("home content") with open(mackup_filepath, "w") as f: f.write("existing backup") with patch("mackup.application.utils.confirm", return_value=False), \ patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: self.app_profile.copy_files_to_mackup_folder() mock_delete.assert_not_called() mock_copy.assert_not_called() with open(mackup_filepath) as f: assert f.read() == "existing backup" def test_copy_files_from_mackup_folder_decline_replace_skips_copy(self): """Test restore does not overwrite when user declines replacement.""" test_file = ".testfile" home_filepath = os.path.join(self.temp_home, test_file) mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) with open(home_filepath, "w") as f: f.write("existing home") with open(mackup_filepath, "w") as f: f.write("backup content") with patch("mackup.application.utils.confirm", return_value=False), \ patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: self.app_profile.copy_files_from_mackup_folder() mock_delete.assert_not_called() mock_copy.assert_not_called() with open(home_filepath) as f: assert f.read() == "existing home" def test_link_uninstall_mackup_not_a_link(self): """Test link_uninstall skips when home file is not a symbolic link.""" # Create a test file in the mackup directory (regular file, not a link) test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create the mackup file as a regular file with open(mackup_filepath, "w") as f: f.write("mackup content") # Create the home file as a regular file (not a link) with open(home_filepath, "w") as f: f.write("home content") # Patch utils.delete and utils.copy with patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.link_uninstall() # Restore stdout sys.stdout = sys.__stdout__ # Verify that delete and copy were NOT called mock_delete.assert_not_called() mock_copy.assert_not_called() # Verify that the warning message was printed output = captured_output.getvalue() assert "Warning: the file in your home" in output assert "does not point to the original file" in output assert mackup_filepath in output assert home_filepath in output assert "skipping" in output def test_link_uninstall_mackup_points_to_wrong_target(self): """Test link_uninstall skips when home link points to wrong target.""" # Create a test file test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create the mackup file with open(mackup_filepath, "w") as f: f.write("mackup content") # Create a different target file wrong_target = os.path.join(self.temp_home, ".wrongtarget") with open(wrong_target, "w") as f: f.write("wrong target content") # Create the home file as a symbolic link pointing to the wrong target os.symlink(wrong_target, home_filepath) # Patch utils.delete and utils.copy with patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.link_uninstall() # Restore stdout sys.stdout = sys.__stdout__ # Verify that delete and copy were NOT called mock_delete.assert_not_called() mock_copy.assert_not_called() # Verify that the warning message was printed output = captured_output.getvalue() assert "Warning: the file in your home" in output assert "does not point to the original file" in output assert mackup_filepath in output assert home_filepath in output assert "skipping" in output def test_link_uninstall_mackup_points_correctly(self): """Test link_uninstall proceeds when home link points to mackup file.""" # Create a test file test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create the mackup file first with open(mackup_filepath, "w") as f: f.write("mackup content") # Create the home file as a symbolic link pointing to the mackup file os.symlink(mackup_filepath, home_filepath) # Patch utils.delete and utils.copy with patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.link_uninstall() # Restore stdout sys.stdout = sys.__stdout__ # Verify that delete and copy WERE called (normal operation) mock_delete.assert_called_once_with(home_filepath) mock_copy.assert_called_once_with(mackup_filepath, home_filepath) # Verify that the reverting message was printed (not warning) output = captured_output.getvalue() assert "Reverting" in output assert "Warning" not in output def test_copy_files_to_mackup_folder_skips_already_linked_files(self): """Test that backup skips files already linked from link install.""" # Create a test file test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create the mackup file first (simulating link install) with open(mackup_filepath, "w") as f: f.write("mackup content") # Create the home file as a symbolic link pointing to the mackup file # (simulating what link install does) os.symlink(mackup_filepath, home_filepath) # Patch utils.delete and utils.copy with patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that delete and copy were NOT called (should skip) mock_delete.assert_not_called() mock_copy.assert_not_called() # Verify that the skipping message was NOT printed (non-verbose) output = captured_output.getvalue() assert "Backing up" not in output # Verify the symlink still exists and points to mackup file assert os.path.islink(home_filepath) assert os.path.samefile(home_filepath, mackup_filepath) # Verify the mackup file still exists with original content assert os.path.exists(mackup_filepath) with open(mackup_filepath) as f: assert f.read() == "mackup content" def test_copy_files_to_mackup_folder_skips_already_linked_files_verbose(self): """Test backup skips files already linked with verbose mode.""" # Create a verbose ApplicationProfile app_profile_verbose = ApplicationProfile( mackup=self.mock_mackup, files=self.test_files, dry_run=False, verbose=True, ) # Create a test file test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create the mackup file first (simulating link install) with open(mackup_filepath, "w") as f: f.write("mackup content") # Create the home file as a symbolic link pointing to the mackup file # (simulating what link install does) os.symlink(mackup_filepath, home_filepath) # Patch utils.delete and utils.copy with patch("mackup.application.utils.delete") as mock_delete, \ patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method app_profile_verbose.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that delete and copy were NOT called (should skip) mock_delete.assert_not_called() mock_copy.assert_not_called() # Verify that the skipping message WAS printed (verbose mode) output = captured_output.getvalue() assert "Skipping" in output assert "already linked to" in output assert home_filepath in output assert mackup_filepath in output # Verify the symlink still exists and points to mackup file assert os.path.islink(home_filepath) assert os.path.samefile(home_filepath, mackup_filepath) # Verify the mackup file still exists with original content assert os.path.exists(mackup_filepath) with open(mackup_filepath) as f: assert f.read() == "mackup content" def test_copy_files_to_mackup_folder_backs_up_symlink_to_different_location(self): """Test that backup still works for symlinks pointing elsewhere (not mackup).""" # Create a test file test_file = ".testfile" mackup_filepath = os.path.join(self.mock_mackup.mackup_folder, test_file) home_filepath = os.path.join(self.temp_home, test_file) # Create a different target file (not in mackup folder) other_target = os.path.join(self.temp_home, ".otherlocation") with open(other_target, "w") as f: f.write("other content") # Create the home file as a symbolic link pointing to different location os.symlink(other_target, home_filepath) # Patch utils.copy (no mackup file exists, so confirm won't be called) with patch("mackup.application.utils.copy") as mock_copy: # Capture stdout captured_output = StringIO() sys.stdout = captured_output # Call the method self.app_profile.copy_files_to_mackup_folder() # Restore stdout sys.stdout = sys.__stdout__ # Verify that copy WAS called (should backup symlinks to other locations) mock_copy.assert_called_once_with(home_filepath, mackup_filepath) # Verify that the backing up message was printed output = captured_output.getvalue() assert "Backing up" in output if __name__ == "__main__": unittest.main() ================================================ FILE: tests/test_appsdb_xdg.py ================================================ """Tests for ApplicationsDatabase XDG support.""" import os import unittest from mackup.appsdb import ApplicationsDatabase class TestApplicationsDatabaseXDG(unittest.TestCase): """Test XDG Base Directory support for custom applications.""" def setUp(self): """Set up test fixtures.""" realpath = os.path.dirname(os.path.realpath(__file__)) self.fixtures_path = os.path.join(realpath, "fixtures") self._original_home = os.environ.get("HOME") self._original_xdg_config_home = os.environ.get("XDG_CONFIG_HOME") os.environ["HOME"] = self.fixtures_path # Clear XDG_CONFIG_HOME to ensure clean state os.environ.pop("XDG_CONFIG_HOME", None) def tearDown(self): """Restore environment variables modified during tests.""" if self._original_home is None: os.environ.pop("HOME", None) else: os.environ["HOME"] = self._original_home if self._original_xdg_config_home is None: os.environ.pop("XDG_CONFIG_HOME", None) else: os.environ["XDG_CONFIG_HOME"] = self._original_xdg_config_home def test_legacy_custom_apps_dir(self): """Test that legacy ~/.mackup/ directory is found.""" # Don't set XDG_CONFIG_HOME, only legacy should be found config_files = ApplicationsDatabase.get_config_files() filenames = {os.path.basename(f) for f in config_files} assert "legacy-test-app.cfg" in filenames def test_xdg_custom_apps_dir(self): """Test that XDG custom apps directory is found.""" xdg_config = os.path.join(self.fixtures_path, "xdg-config-home") os.environ["XDG_CONFIG_HOME"] = xdg_config config_files = ApplicationsDatabase.get_config_files() filenames = {os.path.basename(f) for f in config_files} assert "xdg-test-app.cfg" in filenames def test_legacy_takes_priority_over_xdg(self): """Test that legacy directory takes priority when same app exists.""" xdg_config = os.path.join(self.fixtures_path, "xdg-config-home") os.environ["XDG_CONFIG_HOME"] = xdg_config config_files = ApplicationsDatabase.get_config_files() # Find the priority-test-app.cfg file priority_files = [f for f in config_files if "priority-test-app.cfg" in f] # Should only have one file (legacy should win) assert len(priority_files) == 1 # Should be from legacy directory assert ".mackup" in priority_files[0] assert "xdg-config-home" not in priority_files[0] def test_both_directories_merged(self): """Test that apps from both directories are available.""" xdg_config = os.path.join(self.fixtures_path, "xdg-config-home") os.environ["XDG_CONFIG_HOME"] = xdg_config config_files = ApplicationsDatabase.get_config_files() filenames = {os.path.basename(f) for f in config_files} # Both unique apps should be present assert "legacy-test-app.cfg" in filenames assert "xdg-test-app.cfg" in filenames def test_xdg_default_fallback(self): """Test that XDG falls back to ~/.config when XDG_CONFIG_HOME is not set.""" # Unset XDG_CONFIG_HOME - should fall back to ~/.config os.environ.pop("XDG_CONFIG_HOME", None) # This test just verifies the code doesn't crash # In real scenario, ~/.config/mackup/applications/ would be checked config_files = ApplicationsDatabase.get_config_files() # Should at least contain stock apps and legacy custom apps assert len(config_files) > 0 def test_applications_database_loads_xdg_apps(self): """Test that ApplicationsDatabase correctly loads apps from XDG.""" xdg_config = os.path.join(self.fixtures_path, "xdg-config-home") os.environ["XDG_CONFIG_HOME"] = xdg_config db = ApplicationsDatabase() # XDG app should be loaded assert "xdg-test-app" in db.get_app_names() assert db.get_name("xdg-test-app") == "XDG Test App" def test_applications_database_priority_loads_legacy(self): """Test ApplicationsDatabase loads legacy version when app exists.""" xdg_config = os.path.join(self.fixtures_path, "xdg-config-home") os.environ["XDG_CONFIG_HOME"] = xdg_config db = ApplicationsDatabase() # Priority app should load the legacy version assert "priority-test-app" in db.get_app_names() assert db.get_name("priority-test-app") == "Priority Test App Legacy" if __name__ == "__main__": unittest.main() ================================================ FILE: tests/test_backup_after_link_install.py ================================================ """Test the edge case: running backup after link install.""" import os import shutil import sys import tempfile import unittest from io import StringIO from unittest.mock import Mock from mackup.application import ApplicationProfile from mackup.mackup import Mackup class TestBackupAfterLinkInstall(unittest.TestCase): """Integration test for backup after link install edge case.""" def setUp(self): """Set up test fixtures.""" # Create a mock Mackup instance self.mock_mackup = Mock(spec=Mackup) self.mock_mackup.mackup_folder = tempfile.mkdtemp() # Register cleanup for mackup folder self.addCleanup(self._cleanup_directory, self.mock_mackup.mackup_folder) # Create a temporary home directory self.temp_home = tempfile.mkdtemp() # Register cleanup for temp home self.addCleanup(self._cleanup_directory, self.temp_home) # Save original HOME and set it to temp directory self.original_home = os.environ.get("HOME") os.environ["HOME"] = self.temp_home # Register cleanup for HOME restoration self.addCleanup(self._restore_home, self.original_home) def _cleanup_directory(self, directory): """Safely clean up a directory.""" if os.path.exists(directory): shutil.rmtree(directory) def _restore_home(self, original_home): """Restore the original HOME environment variable.""" if original_home is not None: os.environ["HOME"] = original_home elif "HOME" in os.environ: del os.environ["HOME"] def tearDown(self): """Clean up test fixtures.""" # Cleanup is now handled by addCleanup, which runs even if test fails def test_backup_after_link_install_does_not_delete_mackup_files(self): """ Test the complete scenario: 1. Run link install (moves files to mackup and creates symlinks) 2. Run backup (should skip already linked files) This prevents mackup from trying to delete files in the backup folder. """ # Define test files for this test test_files = {".testfile", ".testdir"} # Step 1: Simulate initial state - files exist in home home_file = os.path.join(self.temp_home, ".testfile") home_dir = os.path.join(self.temp_home, ".testdir") # Create initial files with open(home_file, "w") as f: f.write("original file content") os.makedirs(home_dir) with open(os.path.join(home_dir, "subfile.txt"), "w") as f: f.write("original dir content") # Step 2: Run link install app_profile = ApplicationProfile( mackup=self.mock_mackup, files=test_files, dry_run=False, verbose=False, ) captured_output = StringIO() sys.stdout = captured_output app_profile.link_install() sys.stdout = sys.__stdout__ # Verify link install worked correctly mackup_file = os.path.join(self.mock_mackup.mackup_folder, ".testfile") mackup_dir = os.path.join(self.mock_mackup.mackup_folder, ".testdir") # Files should exist in mackup folder assert os.path.exists(mackup_file) assert os.path.exists(mackup_dir) # Home should have symlinks pointing to mackup assert os.path.islink(home_file) assert os.path.islink(home_dir) assert os.path.samefile(home_file, mackup_file) assert os.path.samefile(home_dir, mackup_dir) # Verify content is preserved with open(mackup_file) as f: assert f.read() == "original file content" with open(os.path.join(mackup_dir, "subfile.txt")) as f: assert f.read() == "original dir content" # Step 3: Run backup (this is where the edge case would occur) captured_output = StringIO() sys.stdout = captured_output app_profile.copy_files_to_mackup_folder() output = captured_output.getvalue() sys.stdout = sys.__stdout__ # Verify backup skipped the already linked files # Should not print "Backing up" for these files assert "Backing up" not in output # Verify the mackup files still exist and weren't deleted assert os.path.exists(mackup_file) assert os.path.exists(mackup_dir) # Verify content is still intact with open(mackup_file) as f: assert f.read() == "original file content" with open(os.path.join(mackup_dir, "subfile.txt")) as f: assert f.read() == "original dir content" # Verify symlinks are still in place assert os.path.islink(home_file) assert os.path.islink(home_dir) assert os.path.samefile(home_file, mackup_file) assert os.path.samefile(home_dir, mackup_dir) def test_backup_after_link_install_verbose_shows_skip_message(self): """Test that verbose mode shows skip messages for already linked files.""" # Define test file for this test test_files = {".testfile"} # Create initial file home_file = os.path.join(self.temp_home, ".testfile") with open(home_file, "w") as f: f.write("test content") # Run link install app_profile = ApplicationProfile( mackup=self.mock_mackup, files=test_files, dry_run=False, verbose=False, ) app_profile.link_install() # Run backup in verbose mode app_profile_verbose = ApplicationProfile( mackup=self.mock_mackup, files=test_files, dry_run=False, verbose=True, ) captured_output = StringIO() sys.stdout = captured_output app_profile_verbose.copy_files_to_mackup_folder() output = captured_output.getvalue() sys.stdout = sys.__stdout__ # Verify skip message is shown assert "Skipping" in output assert "already linked to" in output assert home_file in output if __name__ == "__main__": unittest.main() ================================================ FILE: tests/test_cli.py ================================================ import os import shutil import tempfile import unittest from unittest.mock import patch import pytest from mackup import utils from mackup.main import main class TestCLI(unittest.TestCase): """Test suite for CLI commands: backup, restore, and copy mode workflows.""" def setUp(self): """Set up test environment before each test.""" # Create temporary directories for testing self.test_home = tempfile.mkdtemp(prefix="mackup_test_home_") self.test_storage = tempfile.mkdtemp(prefix="mackup_test_storage_") self.mackup_folder = os.path.join(self.test_storage, "Mackup") # Store original HOME self.original_home = os.environ.get("HOME") self.original_xdg = os.environ.get("XDG_CONFIG_HOME") # Set HOME to our test directory os.environ["HOME"] = self.test_home os.environ["XDG_CONFIG_HOME"] = os.path.join(self.test_home, ".config") # Create test config file self.config_path = os.path.join(self.test_home, ".mackup.cfg") with open(self.config_path, "w") as f: f.write("[storage]\n") f.write("engine = file_system\n") f.write(f"path = {self.test_storage}\n") f.write("directory = Mackup\n") f.write("\n") f.write("[applications_to_sync]\n") f.write("test-app\n") # Create a test application config in the apps database self.test_app_name = "test-app" self.test_file_name = ".testrc" self.test_file_path = os.path.join(self.test_home, self.test_file_name) # Create test file with content with open(self.test_file_path, "w") as f: f.write("test_config=value\n") # Create custom application config self.custom_apps_dir = os.path.join(self.test_home, ".mackup") os.makedirs(self.custom_apps_dir, exist_ok=True) self.custom_app_config = os.path.join(self.custom_apps_dir, "test-app.cfg") with open(self.custom_app_config, "w") as f: f.write("[application]\n") f.write(f"name = {self.test_app_name}\n") f.write("\n") f.write("[configuration_files]\n") f.write(f"{self.test_file_name}\n") # Force yes to all prompts utils.FORCE_YES = True utils.FORCE_NO = False utils.CAN_RUN_AS_ROOT = False def tearDown(self): """Clean up test environment after each test.""" # Restore original HOME if self.original_home: os.environ["HOME"] = self.original_home else: os.environ.pop("HOME", None) # Restore original XDG_CONFIG_HOME if self.original_xdg: os.environ["XDG_CONFIG_HOME"] = self.original_xdg else: os.environ.pop("XDG_CONFIG_HOME", None) # Clean up temporary directories if os.path.exists(self.test_home): shutil.rmtree(self.test_home) if os.path.exists(self.test_storage): shutil.rmtree(self.test_storage) # Reset utils flags utils.FORCE_YES = False utils.FORCE_NO = False utils.CAN_RUN_AS_ROOT = False def test_backup_creates_mackup_folder(self): """Test that mackup backup creates the Mackup folder if it doesn't exist.""" # Ensure Mackup folder doesn't exist assert not os.path.exists(self.mackup_folder) # Mock sys.argv to simulate 'mackup backup' with patch("sys.argv", ["mackup", "backup"]): main() # Check that Mackup folder was created assert os.path.exists(self.mackup_folder) def test_backup_copies_file(self): """Test that mackup backup successfully copies a file to the backup location.""" # Ensure test file exists assert os.path.exists(self.test_file_path) # Run backup with patch("sys.argv", ["mackup", "backup"]): main() # Check that file was copied to Mackup folder backed_up_file = os.path.join(self.mackup_folder, self.test_file_name) assert os.path.exists(backed_up_file) # Verify content is the same with open(self.test_file_path) as f: original_content = f.read() with open(backed_up_file) as f: backed_up_content = f.read() assert original_content == backed_up_content def test_restore_copies_file_back(self): """Test that mackup restore successfully copies a file back from backup.""" # First, create a backup with patch("sys.argv", ["mackup", "backup"]): main() # Verify backup exists backed_up_file = os.path.join(self.mackup_folder, self.test_file_name) assert os.path.exists(backed_up_file) # Remove original file os.remove(self.test_file_path) assert not os.path.exists(self.test_file_path) # Run restore with patch("sys.argv", ["mackup", "restore"]): main() # Check that file was restored assert os.path.exists(self.test_file_path) # Verify content is correct with open(self.test_file_path) as f: restored_content = f.read() assert restored_content == "test_config=value\n" def test_backup_and_restore_full_workflow(self): """Test complete backup and restore workflow.""" original_content = "test_config=value\n" # Verify original file exists and has correct content assert os.path.exists(self.test_file_path) with open(self.test_file_path) as f: assert f.read() == original_content # Step 1: Backup with patch("sys.argv", ["mackup", "backup"]): main() # Verify backup was created backed_up_file = os.path.join(self.mackup_folder, self.test_file_name) assert os.path.exists(backed_up_file) # Step 2: Modify original file modified_content = "test_config=modified\n" with open(self.test_file_path, "w") as f: f.write(modified_content) # Verify file was modified with open(self.test_file_path) as f: assert f.read() == modified_content # Step 3: Restore (should replace modified file with backup) with patch("sys.argv", ["mackup", "restore"]): main() # Verify file was restored to original content with open(self.test_file_path) as f: assert f.read() == original_content def test_backup_preserves_file_permissions(self): """Test that mackup backup preserves file permissions.""" # Set specific permissions on test file os.chmod(self.test_file_path, 0o600) # Run backup with patch("sys.argv", ["mackup", "backup"]): main() # Check backup file permissions backed_up_file = os.path.join(self.mackup_folder, self.test_file_name) assert os.path.exists(backed_up_file) # Verify permissions are preserved (mackup sets to 0600 by default) backed_up_stat = os.stat(backed_up_file) expected_mode = 0o600 assert backed_up_stat.st_mode & 0o777 == expected_mode def test_restore_with_missing_backup(self): """Test that mackup restore handles missing backup files gracefully.""" # Ensure no backup exists assert not os.path.exists(self.mackup_folder) # Create the mackup folder but don't add any files os.makedirs(self.mackup_folder, exist_ok=True) # Run restore (should not crash even though no backup exists) with patch("sys.argv", ["mackup", "restore"]): try: main() # If no exception is raised, the test passes # (restore should gracefully handle missing files) except Exception as e: self.fail(f"Restore raised an exception with missing backup: {e}") def test_backup_with_folder(self): """Test that mackup backup works with folders, not just files.""" # Create a test folder with a file inside test_folder_name = ".test_folder" test_folder_path = os.path.join(self.test_home, test_folder_name) os.makedirs(test_folder_path, exist_ok=True) test_file_in_folder = os.path.join(test_folder_path, "config.txt") with open(test_file_in_folder, "w") as f: f.write("folder_config=value\n") # Update custom app config to include the folder with open(self.custom_app_config, "w") as f: f.write("[application]\n") f.write(f"name = {self.test_app_name}\n") f.write("\n") f.write("[configuration_files]\n") f.write(f"{self.test_file_name}\n") f.write(f"{test_folder_name}\n") # Run backup with patch("sys.argv", ["mackup", "backup"]): main() # Check that folder was copied backed_up_folder = os.path.join(self.mackup_folder, test_folder_name) assert os.path.exists(backed_up_folder) assert os.path.isdir(backed_up_folder) # Check that file inside folder was copied backed_up_file_in_folder = os.path.join(backed_up_folder, "config.txt") assert os.path.exists(backed_up_file_in_folder) # Verify content with open(backed_up_file_in_folder) as f: assert f.read() == "folder_config=value\n" def test_restore_with_folder(self): """Test that mackup restore works with folders.""" # Create a test folder with a file inside test_folder_name = ".test_folder" test_folder_path = os.path.join(self.test_home, test_folder_name) os.makedirs(test_folder_path, exist_ok=True) test_file_in_folder = os.path.join(test_folder_path, "config.txt") with open(test_file_in_folder, "w") as f: f.write("folder_config=value\n") # Update custom app config to include the folder with open(self.custom_app_config, "w") as f: f.write("[application]\n") f.write(f"name = {self.test_app_name}\n") f.write("\n") f.write("[configuration_files]\n") f.write(f"{self.test_file_name}\n") f.write(f"{test_folder_name}\n") # Run backup first with patch("sys.argv", ["mackup", "backup"]): main() # Delete the folder shutil.rmtree(test_folder_path) assert not os.path.exists(test_folder_path) # Run restore with patch("sys.argv", ["mackup", "restore"]): main() # Check that folder was restored assert os.path.exists(test_folder_path) assert os.path.isdir(test_folder_path) # Check that file inside folder was restored assert os.path.exists(test_file_in_folder) # Verify content with open(test_file_in_folder) as f: assert f.read() == "folder_config=value\n" def test_restore_fails_when_mackup_folder_missing(self): """Test that mackup restore fails when Mackup folder doesn't exist.""" # Ensure Mackup folder doesn't exist assert not os.path.exists(self.mackup_folder) # Run restore - should exit with error when backup folder is missing with patch("sys.argv", ["mackup", "restore"]): with pytest.raises(SystemExit) as context: main() # Should exit with non-zero status assert context.value.code != 0 def test_force_and_force_no_are_mutually_exclusive(self): """Passing --force and --force-no together should fail fast.""" with patch("sys.argv", ["mackup", "--force", "--force-no", "backup"]): with pytest.raises(SystemExit) as context: main() assert ( str(context.value) == "Options --force and --force-no are mutually exclusive." ) if __name__ == "__main__": unittest.main() ================================================ FILE: tests/test_config.py ================================================ import os import os.path import unittest from pathlib import Path import pytest from mackup.config import Config, ConfigError from mackup.constants import ( ENGINE_DROPBOX, ENGINE_FS, ENGINE_GDRIVE, ENGINE_ICLOUD, MACKUP_CONFIG_FILE, ) def assert_correct_config_read(testtype): assert testtype == Config()._parser.get("test", "testtype") class TestConfig(unittest.TestCase): def setUp(self): realpath = os.path.dirname(os.path.realpath(__file__)) os.environ["HOME"] = os.path.join(realpath, "fixtures") # these may be set on some user's systems os.environ.pop("XDG_CONFIG_HOME", None) os.environ.pop("MACKUP_CONFIG", None) def test_config_envvar(self): os.environ["MACKUP_CONFIG"] = "~/mackup-envarcheck.cfg" assert_correct_config_read("test_config_envvar") def test_config_xdg(self): os.environ["XDG_CONFIG_HOME"] = "~/xdg-config-home/" assert_correct_config_read("test_config_xdg") def test_config_find_correct_default(self): config_path = Path.home() / MACKUP_CONFIG_FILE try: # create a default config file, this must be cleaned up after the test config_path.write_text("[test]\ntesttype = test_config_default") # nothing else set, should find the default file assert_correct_config_read("test_config_default") # set MACKUP_CONFIG, but should still find the default file os.environ["MACKUP_CONFIG"] = "~/mackup-envarcheck.cfg" assert_correct_config_read("test_config_default") # set XDG_CONFIG_HOME, but should still find the default file os.environ["XDG_CONFIG_HOME"] = "~/xdg-config-home/" assert_correct_config_read("test_config_default") except Exception: raise finally: config_path.unlink(missing_ok=True) assert config_path.exists() is False def test_config_finds_correct_envvar(self): # set XDG_CONFIG_HOME, should find the XDG config file os.environ["XDG_CONFIG_HOME"] = "~/xdg-config-home/" assert_correct_config_read("test_config_xdg") # set MACKUP_CONFIG, should find the MACKUP_CONFIG file os.environ["MACKUP_CONFIG"] = "~/mackup-envarcheck.cfg" assert_correct_config_read("test_config_envvar") def test_config_no_config(self): cfg = Config() # Should should do the same as the default, empty configuration assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) print(cfg.path) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/Mackup" assert cfg.apps_to_ignore == set() assert cfg.apps_to_sync == set() def test_config_empty(self): cfg = Config("mackup-empty.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/Mackup" assert cfg.apps_to_ignore == set() assert cfg.apps_to_sync == set() def test_config_engine_dropbox(self): cfg = Config("mackup-engine-dropbox.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "some_weirld_name" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/some_weirld_name" assert cfg.apps_to_ignore == set() assert cfg.apps_to_sync == set() def test_config_engine_filesystem_absolute(self): cfg = Config("mackup-engine-file_system-absolute.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_FS assert isinstance(cfg.path, str) assert cfg.path == "/some/absolute/folder" assert isinstance(cfg.directory, str) assert cfg.directory == "custom_folder" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/some/absolute/folder/custom_folder" assert cfg.apps_to_ignore == {"subversion", "sequel-pro"} assert cfg.apps_to_sync == set() def test_config_engine_filesystem(self): cfg = Config("mackup-engine-file_system.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_FS assert isinstance(cfg.path, str) assert cfg.path.endswith( os.path.join(os.environ["HOME"], "some/relative/folder"), ) assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == os.path.join( os.environ["HOME"], "some/relative/folder", "Mackup", ) assert cfg.apps_to_ignore == set() assert cfg.apps_to_sync == {"sabnzbd", "sublime-text-3", "x11"} def test_config_engine_google_drive(self): cfg = Config("mackup-engine-google_drive.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_GDRIVE assert isinstance(cfg.path, str) assert cfg.path == "/Users/whatever/Google Drive" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath.endswith("/Google Drive/Mackup") assert cfg.apps_to_ignore == {"subversion", "sequel-pro", "sabnzbd"} assert cfg.apps_to_sync == {"sublime-text-3", "x11", "sabnzbd"} def test_config_engine_icloud(self): cfg = Config("mackup-engine-icloud.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_ICLOUD assert isinstance(cfg.path, str) assert cfg.path == os.path.expanduser( "~/Library/Mobile Documents/com~apple~CloudDocs/", ) assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath.endswith("/com~apple~CloudDocs/Mackup") assert cfg.apps_to_ignore == {"subversion", "sequel-pro", "sabnzbd"} assert cfg.apps_to_sync == {"sublime-text-3", "x11", "sabnzbd"} def test_config_engine_filesystem_no_path(self): with pytest.raises(ConfigError): Config("mackup-engine-file_system-no_path.cfg") def test_config_engine_unknown(self): with pytest.raises(ConfigError): Config("mackup-engine-unknown.cfg") def test_config_apps_to_ignore(self): cfg = Config("mackup-apps_to_ignore.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/Mackup" assert cfg.apps_to_ignore == {"subversion", "sequel-pro", "sabnzbd"} assert cfg.apps_to_sync == set() def test_config_apps_to_sync(self): cfg = Config("mackup-apps_to_sync.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/Mackup" assert cfg.apps_to_ignore == set() assert cfg.apps_to_sync == {"sabnzbd", "sublime-text-3", "x11"} def test_config_apps_to_ignore_and_sync(self): cfg = Config("mackup-apps_to_ignore_and_sync.cfg") assert isinstance(cfg.engine, str) assert cfg.engine == ENGINE_DROPBOX assert isinstance(cfg.path, str) assert cfg.path == "/home/some_user/Dropbox" assert isinstance(cfg.directory, str) assert cfg.directory == "Mackup" assert isinstance(cfg.fullpath, str) assert cfg.fullpath == "/home/some_user/Dropbox/Mackup" assert cfg.apps_to_ignore == {"subversion", "sequel-pro", "sabnzbd"} assert cfg.apps_to_sync == {"sabnzbd", "sublime-text-3", "x11", "vim"} def test_config_old_config(self): with pytest.raises(SystemExit): Config("mackup-old-config.cfg") ================================================ FILE: tests/test_config_file_option.py ================================================ """Tests for the --config-file command line option.""" import os import unittest import pytest from mackup.config import Config from mackup.mackup import Mackup class TestConfigFileOption(unittest.TestCase): def setUp(self): realpath = os.path.dirname(os.path.realpath(__file__)) os.environ["HOME"] = os.path.join(realpath, "fixtures") # Clear environment variables that could interfere os.environ.pop("XDG_CONFIG_HOME", None) os.environ.pop("MACKUP_CONFIG", None) def test_config_with_relative_path(self): """Test that a relative path to config file works.""" cfg = Config("mackup-apps_to_ignore.cfg") assert cfg.apps_to_ignore == {"subversion", "sequel-pro", "sabnzbd"} def test_config_with_absolute_path(self): """Test that an absolute path to config file works.""" abs_path = os.path.join(os.environ["HOME"], "mackup-apps_to_sync.cfg") cfg = Config(abs_path) assert cfg.apps_to_sync == {"sabnzbd", "sublime-text-3", "x11"} def test_mackup_with_config_file(self): """Test that Mackup class accepts config_file parameter.""" # This should not raise any errors mckp = Mackup("mackup-empty.cfg") # Verify that the config was properly initialized assert mckp._config is not None assert isinstance(mckp.mackup_folder, str) def test_mackup_without_config_file(self): """Test that Mackup class works without config_file parameter.""" # This should use default config file discovery mckp = Mackup() # Verify that the config was properly initialized assert mckp._config is not None assert isinstance(mckp.mackup_folder, str) def test_config_file_does_not_exist(self): """Test that specifying a non-existent config file raises an error.""" with pytest.raises(SystemExit): Config("nonexistent-config-file.cfg") ================================================ FILE: tests/test_constants.py ================================================ import unittest from unittest.mock import patch from mackup import constants class TestConstants(unittest.TestCase): def test_get_version_returns_metadata_version(self): with patch("mackup.constants.version", return_value="1.2.3"): assert constants._get_version() == "1.2.3" def test_get_version_falls_back_when_metadata_missing(self): with patch( "mackup.constants.version", side_effect=constants.PackageNotFoundError("mackup"), ): assert constants._get_version() == "unknown" ================================================ FILE: tests/test_main.py ================================================ import unittest from mackup import main class TestMain(unittest.TestCase): def test_main_header(self): assert main.header("blah") == "\033[34mblah\033[0m" def test_main_bold(self): assert main.bold("blah") == "\033[1mblah\033[0m" ================================================ FILE: tests/test_utils.py ================================================ import os import sqlite3 import stat import tempfile import unittest from unittest.mock import patch import pytest from mackup import utils def convert_to_octal(file_name): """ Using os.stat, returns file permissions (read, write, execute) as an octal. """ return oct(os.stat(file_name)[stat.ST_MODE])[-3:] class TestMackup(unittest.TestCase): def test_confirm_yes(self): # Override the input used in utils with patch.object(utils, "input", return_value="Yes", create=True): assert utils.confirm("Answer Yes to this question") def test_confirm_no(self): # Override the input used in utils with patch.object(utils, "input", return_value="No", create=True): assert not utils.confirm("Answer No to this question") def test_confirm_typo(self): # Override the input used in utils with patch.object(utils, "input", return_value="No", create=True): assert not utils.confirm("Answer garbage to this question") def test_delete_file(self): # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False) tfpath = tfile.name tfile.close() # Make sure the created file exists assert os.path.isfile(tfpath) # Check if mackup can really delete it utils.delete(tfpath) assert not os.path.exists(tfpath) def test_delete_folder_recursively(self): # Create a tmp folder tfpath = tempfile.mkdtemp() # Let's put a file in it just for fun tfile = tempfile.NamedTemporaryFile(dir=tfpath, delete=False) filepath = tfile.name tfile.close() # Let's put another folder in it subfolder_path = tempfile.mkdtemp(dir=tfpath) # And a file in the subfolder tfile = tempfile.NamedTemporaryFile(dir=subfolder_path, delete=False) subfilepath = tfile.name tfile.close() # Make sure the created files and folders exists assert os.path.isdir(tfpath) assert os.path.isfile(filepath) assert os.path.isdir(subfolder_path) assert os.path.isfile(subfilepath) # Check if mackup can really delete it utils.delete(tfpath) assert not os.path.exists(tfpath) assert not os.path.exists(filepath) assert not os.path.exists(subfolder_path) assert not os.path.exists(subfilepath) def test_copy_file(self): # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False) srcfile = tfile.name tfile.close() # Create a tmp folder dstpath = tempfile.mkdtemp() # Set the destination filename dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile)) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Check if mackup can copy it utils.copy(srcfile, dstfile) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert os.path.exists(dstfile) # Let's clean up utils.delete(dstpath) def test_copy_fail(self): # Create a tmp FIFO file tfile = tempfile.NamedTemporaryFile() srcfile = tfile.name tfile.close() os.mkfifo(srcfile) # Create a tmp folder dstpath = tempfile.mkdtemp() # Set the destination filename dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile)) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert not os.path.isfile(srcfile) assert stat.S_ISFIFO(os.stat(srcfile).st_mode) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Check if mackup can copy it with pytest.raises(ValueError, match="Unsupported file"): utils.copy(srcfile, dstfile) assert not os.path.isfile(srcfile) assert stat.S_ISFIFO(os.stat(srcfile).st_mode) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Let's clean up utils.delete(srcfile) utils.delete(dstpath) def test_copy_file_to_dir(self): """Copies a file to a destination folder that already exists.""" # Create a tmp folder srcpath = tempfile.mkdtemp() # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False, dir=srcpath) srcfile = tfile.name tfile.close() # Create a tmp folder dstpath = tempfile.mkdtemp() # Set the destination filename srcpath_basename = os.path.basename(srcpath) dstfile = os.path.join( dstpath, "subfolder", srcpath_basename, os.path.basename(srcfile), ) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert os.path.isdir(srcpath) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Check if mackup can copy it utils.copy(srcfile, dstfile) assert os.path.isdir(srcpath) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert os.path.exists(dstfile) # Let's clean up utils.delete(srcpath) utils.delete(dstpath) def test_copy_dir(self): """Copies a directory recursively to the destination path.""" # Create a tmp folder srcpath = tempfile.mkdtemp() # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False, dir=srcpath) srcfile = tfile.name tfile.close() # Create a tmp folder dstpath = tempfile.mkdtemp() # Set the destination filename srcpath_basename = os.path.basename(srcpath) dstfile = os.path.join(dstpath, srcpath_basename, os.path.basename(srcfile)) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert os.path.isdir(srcpath) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Check if mackup can copy it utils.copy(srcpath, dstfile) assert os.path.isdir(srcpath) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert os.path.exists(dstfile) # Let's clean up utils.delete(srcpath) utils.delete(dstpath) def test_copy_dir_that_exists(self): """Copies a directory recursively to an existing destination.""" # Create a source and a destination tmp folders src_folder = tempfile.mkdtemp() dst_folder = tempfile.mkdtemp() # Create a folder inside the source folder src_subfolder = tempfile.mkdtemp(dir=src_folder) # Create the same subfolder inside the destination folder src_subfolder_name = os.path.basename(src_subfolder) dst_subfolder = os.path.join(dst_folder, src_subfolder_name) os.mkdir(dst_subfolder) # Create a tmp file in the src subfolder src_file = tempfile.NamedTemporaryFile(delete=False, dir=src_subfolder) src_file_name = src_file.name src_file.close() # Set the destination filename dst_file = os.path.join(dst_subfolder, os.path.basename(src_file_name)) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert os.path.isdir(src_folder) assert os.path.isdir(src_subfolder) assert os.path.isfile(src_file_name) assert os.path.isdir(dst_folder) assert os.path.isdir(dst_subfolder) assert not os.path.exists(dst_file) # Check if mackup can copy it utils.copy(src_folder, dst_folder) assert os.path.isdir(src_folder) assert os.path.isdir(src_subfolder) assert os.path.isfile(src_file_name) assert os.path.isdir(dst_folder) assert os.path.isdir(dst_subfolder) assert os.path.exists(dst_file) # Let's clean up utils.delete(src_folder) utils.delete(dst_folder) def test_link_file(self): # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False) srcfile = tfile.name tfile.close() # Create a tmp folder dstpath = tempfile.mkdtemp() # Set the destination filename dstfile = os.path.join(dstpath, "subfolder", os.path.basename(srcfile)) # Make sure the source file and destination folder exist and the # destination file doesn't yet exist assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert not os.path.exists(dstfile) # Check if mackup can link it and the link points to the correct place utils.link(srcfile, dstfile) assert os.path.isfile(srcfile) assert os.path.isdir(dstpath) assert os.path.exists(dstfile) assert os.readlink(dstfile) == srcfile # Let's clean up utils.delete(dstpath) def test_chmod_file(self): # Create a tmp file tfile = tempfile.NamedTemporaryFile(delete=False) file_name = tfile.name # Create a tmp directory with a sub folder dir_name = tempfile.mkdtemp() nested_dir = tempfile.mkdtemp(dir=dir_name) # # File Tests # Change the tmp file stats to S_IWRITE (200), write access only os.chmod(file_name, stat.S_IWRITE) assert convert_to_octal(file_name) == "200" # Check to make sure that utils.chmod changes the bits to 600, # which is read and write access for the owner utils.chmod(file_name) assert convert_to_octal(file_name) == "600" # # Directory Tests # Change the tmp folder stats to S_IREAD (400), read access only os.chmod(dir_name, stat.S_IREAD) assert convert_to_octal(dir_name) == "400" # Check to make sure that utils.chmod changes the bits of all # directories to 700, which is read, write, and execute access for the # owner utils.chmod(dir_name) assert convert_to_octal(dir_name) == "700" assert convert_to_octal(nested_dir) == "700" # Use an "unsupported file type". In this case, /dev/null with pytest.raises(ValueError, match="Unsupported file type"): utils.chmod(os.devnull) def test_error(self): test_string = "Hello World" with pytest.raises(SystemExit): utils.error(test_string) def test_failed_backup_location(self): """ Tests for the error that should occur if the backup folder cannot be found for Dropbox and Google """ # Hack to make our home folder some temporary folder temp_home = tempfile.mkdtemp() utils.os.environ["HOME"] = temp_home # Check for the missing Dropbox folder assert not os.path.exists(os.path.join(temp_home, ".dropbox/host.db")) with pytest.raises(SystemExit): utils.get_dropbox_folder_location() # Check for the missing Google Drive folder assert not os.path.exists( os.path.join( temp_home, "Library/Application Support/Google/Drive/sync_config.db", ), ) with pytest.raises(SystemExit): utils.get_google_drive_folder_location() def test_dropbox_folder_location_with_malformed_host_db(self): """Malformed Dropbox host.db should fail with a user-facing error.""" with tempfile.TemporaryDirectory() as temp_home, patch.dict( os.environ, {"HOME": temp_home}, ): host_db_path = os.path.join(temp_home, ".dropbox", "host.db") os.makedirs(os.path.dirname(host_db_path), exist_ok=True) with open(host_db_path, "w") as f: f.write("malformed-content-without-base64-path") with pytest.raises(SystemExit): utils.get_dropbox_folder_location() def test_dropbox_folder_location_with_invalid_base64(self): """Invalid base64 in Dropbox host.db should fail with a user-facing error.""" with tempfile.TemporaryDirectory() as temp_home, patch.dict( os.environ, {"HOME": temp_home}, ): host_db_path = os.path.join(temp_home, ".dropbox", "host.db") os.makedirs(os.path.dirname(host_db_path), exist_ok=True) with open(host_db_path, "w") as f: f.write("first-field invalid-base64-!@#$") with pytest.raises(SystemExit): utils.get_dropbox_folder_location() def test_google_drive_folder_location_with_missing_path_entry(self): """Google Drive DB without local_sync_root_path should fail cleanly.""" with tempfile.TemporaryDirectory() as temp_home, patch.dict( os.environ, {"HOME": temp_home}, ): gdrive_db = os.path.join( temp_home, "Library/Application Support/Google/Drive/sync_config.db", ) os.makedirs(os.path.dirname(gdrive_db), exist_ok=True) con = sqlite3.connect(gdrive_db) cur = con.cursor() cur.execute("CREATE TABLE data (entry_key TEXT, data_value TEXT)") cur.execute( "INSERT INTO data (entry_key, data_value) VALUES (?, ?)", ("another_key", "/tmp/whatever"), ) con.commit() con.close() with pytest.raises(SystemExit): utils.get_google_drive_folder_location() def test_google_drive_folder_location_uses_user_default_db(self): """Read Google Drive location from user_default sync DB when present.""" with tempfile.TemporaryDirectory() as temp_home, patch.dict( os.environ, {"HOME": temp_home}, ): gdrive_db = os.path.join( temp_home, "Library/Application Support/Google/Drive/user_default/sync_config.db", ) os.makedirs(os.path.dirname(gdrive_db), exist_ok=True) expected_path = os.path.join(temp_home, "Google Drive") con = sqlite3.connect(gdrive_db) cur = con.cursor() cur.execute("CREATE TABLE data (entry_key TEXT, data_value TEXT)") cur.execute( "INSERT INTO data (entry_key, data_value) VALUES (?, ?)", ("local_sync_root_path", expected_path), ) con.commit() con.close() assert utils.get_google_drive_folder_location() == expected_path def test_is_process_running(self): # A pgrep that has one letter and a wildcard will always return id 1 assert utils.is_process_running("a*") assert not utils.is_process_running("some imaginary process") def test_can_file_be_synced_on_current_platform(self): # Any file path will do, even if it doesn't exist path = "some/file" # Force the Mac OSX Test using mock with patch.object( utils.platform, "system", return_value=utils.constants.PLATFORM_DARWIN, ): assert utils.can_file_be_synced_on_current_platform(path) # Force the Linux Test using mock with patch.object( utils.platform, "system", return_value=utils.constants.PLATFORM_LINUX, ): assert utils.can_file_be_synced_on_current_platform(path) # Try to use the library path on Linux, which shouldn't work path = os.path.join(os.environ["HOME"], "Library/") assert not utils.can_file_be_synced_on_current_platform(path)