{ "info": { "author": "Mike Durso", "author_email": "rbprogrammer@gmail.com", "bugtrack_url": null, "classifiers": [ "Environment :: Console", "Intended Audience :: Developers", "Intended Audience :: System Administrators", "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: MacOS", "Operating System :: Microsoft :: Windows", "Operating System :: POSIX", "Operating System :: POSIX :: Linux", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 3", "Topic :: Utilities" ], "description": "# dfm\n\n[![pipeline](https://gitlab.com/deliberist/dfmpy/badges/main/pipeline.svg)](https://gitlab.com/deliberist/dfmpy/-/commits/main) \n[![Build status](https://ci.appveyor.com/api/projects/status/gie9q4210vr7usf0?svg=true)](https://ci.appveyor.com/project/rbprogrammer/dfmpy)\n[![codecov](https://codecov.io/gl/deliberist/dfmpy/branch/main/graph/badge.svg?token=8zT6EYk0if)](https://codecov.io/gl/deliberist/dfmpy)\n[![pypi](https://img.shields.io/pypi/v/dfmpy.svg)](https://pypi.org/project/dfmpy)\n\nSometimes pronounced \"_diff-em-py_\" and is a small play on words. dfm is yet\nanother Dot-Files Manager, but with robust features that can essentially\n\"_diff_\" the dotfiles actually installed versus the expected installed files.\nHence the \"_diff-em_\".\n\ndfm is not just another dotfiles manager, it supports multiple environments\nbased on hostname, system type, or a combination of the two. It also ignores\nfiles/directories based on user defined globs which can be particularly useful\nif dfm has to traverse directories that have large I/O latencies (large\ndirectory inodes, too many files to stat, or even just network mounted\ndirectories, etc.).\n\n# Installation\n\nSince dfm is [registered on Pypi](https://pypi.org/project/dfm), you can install\ndfm through standard `pip` methods.\n\n```bash\n# Install locally as a normal user:\npip3 install --user --force --upgrade dfmpy\n\n# Or install globally, as the all powerful root, for all users of the system:\nsudo pip3 install --force --upgrade dfmpy\n```\n\nFor more installation details, see \n[INSTALLATION.md](https://gitlab.com/deliberist/dfmpy/blob/main/INSTALLATION.md).\n\n# Config Files\n\nTo use dfm you must first `dfm init` which will create two files config\nfiles:\n\n1. `${XDG_CONFIG_HOME}/dfm/config.ini`\n1. `${XDG_CONFIG_HOME}/dfm/ignore.globs`\n\nwhere `${XDG_CONFIG_HOME}` is usually `~/.config`. dfm uses\n[xdgenvpy](https://pypi.org/project/xdgenvpy) to get a handle on the dfm config\ndirectory.\n\n`config.ini` is the main config file that tells it, among other things, where to\nfind your dotfiles repository, where to install symlinks, and even how to manage\nidentical filenames for different systems.\n\n`ignore.globs` is a set of commonly ignored files and directories that one might\nnever want synced with their dotfiles repository. For example, if you manage\nyour dotfiles repository with Git then you would never want the `.git` directory\nsymlinked to your destination directory. And vice versa, you should never add\nSSH keys (`~/.ssh/id_*`) to your dotfiles repo. The globs in this file tell\ndfm to just skip over the files or directories.\n\n# Usage\n\ndfm makes use of Python's\n[argparse](https://docs.python.org/3/library/argparse.html) library sub-command\nfeature. This gives dfm multiple independent commands with their own set of\nCLI arguments.\n\n## File suffixes\n\nTo maintain dotfiles across multiple systems there needs to be a mechanism that\nallows for all the files to have the same name when install but not collide when\nthey reside in the repository. To get around this dfm makes use of the system's\nhostname and system type appended to the file name.\n\nFor example, developers may want a `~/.vimrc` in their home directory. But in \nthe dotfiles repository we may want all vimrc files next to each other, i.e. in\nthe same directory. dfm searches for a `##` marker in a file to determine if\nthe file has a system specific variant. Suppose our dotfiles repo looks like\nthis:\n\n```bash\ncd ~/.files\ntree\n.\n|-- .vimrc\n|-- .vimrc##host1.Linux\n|-- .vimrc##host2.Linux\n|-- .vimrc##host2.Windows\n|-- .vimrc##host3\n`-- .vimrc##Windows\n\n0 directories, 6 files\n```\n\n- `.vimrc` is the default vimrc file and will be the symlinked file if no other\n system specific file exists.\n\n- `.vimrc##host1.Linux` is a system specific file that will only be symlinked if\n the hostname is \"host1\" and the system type is Linux.\n\n- `.vimrc##host2.Linux` is a system specific file that will only be symlinked if\n the hostname is \"host2\" and the system type is Linux.\n\n- `.vimrc##host2.Windows` is a system specific file that will only be symlinked\n if the hostname is \"host2\" and the system type is Windows.\n\n- `.vimrc##host3` is a system specific file that will only be symlinked if the\n hostname is \"host3\".\n\n- `.vimrc##Windows` is a system specific file that will only be symlinked if the\n system type is Windows.\n\nThe hostname is determined by the return value from\n[socket.gethostname()](https://docs.python.org/3/library/socket.html#socket.gethostname),\nand the system type is determined by the return value from\n[platform.system()](https://docs.python.org/3/library/platform.html#platform.system).\n\n## Common arguments\n\n```bash\ndfm --help\n```\n\n`-v` is the verbose flag, which can be used multiple times. This flag controls\nwhich log level gets printed. The default log level is set to ERROR, which is\nlowered to WARNING when one `-v` flag is set. Multiple `-v` flags will lower\nthe log level even further.\n\n`-i` turns on interactive mode. dfm strives to be as filesystem safe as\npossible. By default it will not attempt to overwrite files. The interactive\nflag tells dfm to ask for permission when it performs a potentially dangerous\noperation.\n\n`-f` turns on force mode. Again, dfm strives to be filesystem safe. The\ndefault commands will not overwrite files nor delete files without explicit user\ndirection. While interactive mode can help with this, sometimes developers want\nto just force an operation and live with the consequences. Effectively, force\nmode short circuits interactive mode and assumes the developers accepts the\noperation that dfm is trying to perform (e.g. overwriting a file).\n\n## Install dfm\n\n```bash\ndfm init --help\n```\n\nCurrently, dfm requires initialization after installation. We merely need to\nrun the `init` command.\n\n```bash\npip3 install --user --force --upgrade dfmpy\ndfm init\n```\n\n## Sync your dotfiles\n\n```bash\ndfm sync --help\n```\n\nOnce installed and initialized, dfm will utilize the\n`${XDG_CONFIG_HOME}/dfm/config.ini` config file when it needs to (re)sync your\ndotfiles. Per the default `config.ini` file, dfm assumes your dotfiles repo is\ninitially located at `~/.local/share/dotfiles`. If this is not the case, you\nneed to modify your `config.ini` accordingly.\n\n```bash\ndfm sync -f\n```\n\nThe sync command will use the `dfm/config.ini` file to determine where the\ndotfiles are installed and where the dotfiles repository is. It will then \ncalculate a set of expected symlinks to files. dfm uses this set to traverse\nthe installed dotfiles to determine what needs updating.\n\nThe sync command will create new symlinks to the expected files in the dotfiles\nrepository. However, being filesystem safe, the sync command will not unlink\nexisting symlinks, nor overwrite existing symlinks. Either interactive or force\nmode is required to make such changes.\n\n## Adding individual files\n\n```bash\ndfm add --help\n```\n\nSometimes developers want to add a single file to their dotfiles repository.\ndfm has an option to add the file from their home directory directly into their\ndotfiles repository, then automatically symlink so a system specific file.\n\nFor example, let's say we needed to add our `~/.vimrc` file to our dotfiles. \nThe `$HOME` directory may look roughly like this:\n\n```bash\nls -al ~\ndrwxr-xr-x 22 user user 4096 Nov 10 11:47 .\ndrwxr-xr-x 3 root root 4096 Apr 25 2019 ..\n...\ndrwxr-xr-x 13 user user 4096 Jul 9 04:54 .cache\ndrwxr-xr-x 30 user user 4096 Nov 10 11:47 .config\ndrwx------ 6 user user 4096 Nov 10 11:47 .local\n-rw------- 1 user user 57 Oct 23 19:13 .vimrc\n...\n```\n\nWe can simply add the `~/.vimrc` file, and the developer's home directory will\nlook like this:\n\n```bash\ndfm add ~/.vimrc\nls -al ~\ndrwxr-xr-x 22 user user 4096 Nov 10 11:47 .\ndrwxr-xr-x 3 root root 4096 Apr 25 2019 ..\n...\ndrwxr-xr-x 13 user user 4096 Jul 9 04:54 .cache\ndrwxr-xr-x 30 user user 4096 Nov 10 11:47 .config\ndrwx------ 6 user user 4096 Nov 10 11:47 .local\nlrwxrwxrwx 1 user user 28 Nov 10 11:47 .vimrc -> /home/user/.files/.vimrc##hostname.Linux\n...\n```\n\nUnder the hood, dfm is simply moving the file into the dotfiles repository with\nthe most restrictive system specific name. Then it will create the symlink so\nthat `~/.vimrc` points to the repository file.\n\n## Listing the installed (eg synced) files\n\n```bash\ndfm list --help\n```\n\ndfm has a unique insight into your dotfiles. It knows how to ignore certain\nfiles, it knows what files should be symlinked to others, and it knows when\nthere is a discrepancy with the installed files versus the dotfiles repo. As\nsuch, simple Bash `ls -R` or `tree` commands will not print just the dotfiles\nmanaged by dfm.\n\ndfm has a `list` command that prints only the files dfm manages, the files it\nexpects, and the files that might have broken symlinks. The file listing also\nadheres to dfm's log level conventions:\n\n- ***broken symlinks*** (links to non-existent files) are logged at the CRITICAL\n level.\n- ***stale symlinks*** (links to the wrong files) are logged at the ERROR level.\n- ***not installed symlinks*** are logged at the WARNING level.\n- and ***proper symlinks*** (links to the correct files) are logged at the INFO\n level.\n\nAdditionally, the list command has a `--tree` mode that changes the output into\na directory tree structure, rather than a strict list.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://gitlab.com/rbprogrammer/dfmpy", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "dfmpy", "package_url": "https://pypi.org/project/dfmpy/", "platform": "", "project_url": "https://pypi.org/project/dfmpy/", "project_urls": { "Homepage": "https://gitlab.com/rbprogrammer/dfmpy" }, "release_url": "https://pypi.org/project/dfmpy/0.0.5/", "requires_dist": null, "requires_python": "", "summary": "Another dotfiles manager.", "version": "0.0.5", "yanked": false, "yanked_reason": null }, "last_serial": 8965663, "releases": { "0.0.0": [ { "comment_text": "", "digests": { "md5": "232f16c43377e0acfb7a07559a62b3fb", "sha256": "051d2087298a9bab16a4ff4c55e72ef1158c71c8e7e450c420f7b7bd8fbb4544" }, "downloads": -1, "filename": "dfmpy-0.0.0-py3-none-any.whl", "has_sig": false, "md5_digest": "232f16c43377e0acfb7a07559a62b3fb", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25050, "upload_time": "2019-10-24T00:50:10", "upload_time_iso_8601": "2019-10-24T00:50:10.061442Z", "url": "https://files.pythonhosted.org/packages/61/9b/1bfc2aa852d8faec20c423d3c12898a8c000612dd22aa15014f7fffb1bb0/dfmpy-0.0.0-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "2244be8e748cd6118827cb131a99a94b", "sha256": "9427a6e5fead9c8d7ddd012e2fdb3edf79aaee9355fc3e5cf4ad82bcdd8d2fff" }, "downloads": -1, "filename": "dfmpy-0.0.0.tar.gz", "has_sig": false, "md5_digest": "2244be8e748cd6118827cb131a99a94b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10536, "upload_time": "2019-10-24T00:50:12", "upload_time_iso_8601": "2019-10-24T00:50:12.599877Z", "url": "https://files.pythonhosted.org/packages/bb/7a/b30e89fe3efa8d30acaa17ad1a8466a99cd22e4c361241d06e02eafa0453/dfmpy-0.0.0.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.1": [ { "comment_text": "", "digests": { "md5": "7cc6575db83008227c8bab9594332961", "sha256": "0e973e881bf1603dbc86a99677787e3863375bdaf5790736e50667e070075e97" }, "downloads": -1, "filename": "dfmpy-0.0.1-py3-none-any.whl", "has_sig": false, "md5_digest": "7cc6575db83008227c8bab9594332961", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 25087, "upload_time": "2019-10-24T02:00:33", "upload_time_iso_8601": "2019-10-24T02:00:33.542783Z", "url": "https://files.pythonhosted.org/packages/df/61/ecd7bf17f4e2fdf1ae24ce4715406a6413629583a94a6fbf53258f2dceec/dfmpy-0.0.1-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "a5cbc8416cf0ac0736e4032fb4732d1c", "sha256": "85426d873339d5eebaab012b744c017f41d54b4529d06c8c398a3992af9a09be" }, "downloads": -1, "filename": "dfmpy-0.0.1.tar.gz", "has_sig": false, "md5_digest": "a5cbc8416cf0ac0736e4032fb4732d1c", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 10550, "upload_time": "2019-10-24T02:00:34", "upload_time_iso_8601": "2019-10-24T02:00:34.989003Z", "url": "https://files.pythonhosted.org/packages/25/26/ac26212eb99b7ba900a5c51b4abbf7588651724842f42ce44e12f8624a88/dfmpy-0.0.1.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.2": [ { "comment_text": "", "digests": { "md5": "5458299475a6d06a1aac7a0446a81295", "sha256": "eb47ead4edae22d0ee98ff68569473f183153cd1eac846ac963fd4a6237ba0f7" }, "downloads": -1, "filename": "dfmpy-0.0.2-py3-none-any.whl", "has_sig": false, "md5_digest": "5458299475a6d06a1aac7a0446a81295", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 26695, "upload_time": "2019-11-09T02:50:22", "upload_time_iso_8601": "2019-11-09T02:50:22.657164Z", "url": "https://files.pythonhosted.org/packages/2d/94/3bb48a1e512183081751b97b7f1d611313760b74c8f9a12241e810a499d6/dfmpy-0.0.2-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "096371033adae113421dd554d9c9896a", "sha256": "60b0d8406a2b832d8c013f830bda4ed40715c9dee6a31ea21e38165ae7c42a41" }, "downloads": -1, "filename": "dfmpy-0.0.2.tar.gz", "has_sig": false, "md5_digest": "096371033adae113421dd554d9c9896a", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 11434, "upload_time": "2019-11-09T02:50:24", "upload_time_iso_8601": "2019-11-09T02:50:24.258182Z", "url": "https://files.pythonhosted.org/packages/0c/c1/9b7dd2862679bfd21d8a07f10fb8de3400363ba3bba91151fdffbe3f5bf3/dfmpy-0.0.2.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.3": [ { "comment_text": "", "digests": { "md5": "6211cb6ec48e9faa6276dd5ffc28a866", "sha256": "50f6b65d752c4277624a95d2234810de6b6a7867fee5492cc66d7bd7f6e30b7e" }, "downloads": -1, "filename": "dfmpy-0.0.3-py3-none-any.whl", "has_sig": false, "md5_digest": "6211cb6ec48e9faa6276dd5ffc28a866", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 31001, "upload_time": "2019-11-10T19:31:38", "upload_time_iso_8601": "2019-11-10T19:31:38.199330Z", "url": "https://files.pythonhosted.org/packages/5f/9b/ac69cdf28b782eee36fbe63a9de7589d80afef4ce581fffc226e99ed1c8a/dfmpy-0.0.3-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "2f3bc3e907b5b89b5f68381a76df6f18", "sha256": "0bef0e217a5b8e795c257117b6946b2b97df6cfedd698a636a399ab2041cbd56" }, "downloads": -1, "filename": "dfmpy-0.0.3.tar.gz", "has_sig": false, "md5_digest": "2f3bc3e907b5b89b5f68381a76df6f18", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 14991, "upload_time": "2019-11-10T19:31:39", "upload_time_iso_8601": "2019-11-10T19:31:39.486366Z", "url": "https://files.pythonhosted.org/packages/32/f0/be95ebbd6ad1f47111339cf1bce709e7ccba1bfdf6ca74cd9bccfcbf1880/dfmpy-0.0.3.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.4": [ { "comment_text": "", "digests": { "md5": "9d25ca6c5dc2dd52c0358228f869a523", "sha256": "ca832564eceef66f9398157f0a578914258ba0c4d3445d2f0aa9072e1985c2fc" }, "downloads": -1, "filename": "dfmpy-0.0.4-py3-none-any.whl", "has_sig": false, "md5_digest": "9d25ca6c5dc2dd52c0358228f869a523", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 32982, "upload_time": "2020-01-12T17:58:46", "upload_time_iso_8601": "2020-01-12T17:58:46.091728Z", "url": "https://files.pythonhosted.org/packages/4f/a7/b28d673f32c3882409c57a5fbea7464ba2b1e38d3b3958df392311f5e7d5/dfmpy-0.0.4-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "fd47e9bd6c2c6bfff7432f8c2bd128da", "sha256": "7530db2e920a84930dfaae1a4cc7fd9c11d0faea9ca609191b7d530c42bccee3" }, "downloads": -1, "filename": "dfmpy-0.0.4.tar.gz", "has_sig": false, "md5_digest": "fd47e9bd6c2c6bfff7432f8c2bd128da", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 16538, "upload_time": "2020-01-12T17:58:47", "upload_time_iso_8601": "2020-01-12T17:58:47.571284Z", "url": "https://files.pythonhosted.org/packages/d0/0f/6019cbef361ea2983868b68de0e5d64a3daec27b209bee54e98a5184a859/dfmpy-0.0.4.tar.gz", "yanked": false, "yanked_reason": null } ], "0.0.5": [ { "comment_text": "", "digests": { "md5": "8f6911a920d4069e5c443f526a8c4e90", "sha256": "8c83e9e52e8186d1e4d75fca794620c173c433d1c29bbfd126c8c377eb46f96a" }, "downloads": -1, "filename": "dfmpy-0.0.5-py3-none-any.whl", "has_sig": false, "md5_digest": "8f6911a920d4069e5c443f526a8c4e90", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 22244, "upload_time": "2020-12-23T01:07:43", "upload_time_iso_8601": "2020-12-23T01:07:43.831048Z", "url": "https://files.pythonhosted.org/packages/18/ae/a8f4661b8f28ebdfa9bdd1a13ce84a67c706d191f89b3902424e5daa30cd/dfmpy-0.0.5-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "5351957f5aa9cdaa572091a2bd7db786", "sha256": "4ef287de306ed407864e45f9b64e46ffbc644ca4cfcf96be0754e58b9e529504" }, "downloads": -1, "filename": "dfmpy-0.0.5.tar.gz", "has_sig": false, "md5_digest": "5351957f5aa9cdaa572091a2bd7db786", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21881, "upload_time": "2020-12-23T01:07:44", "upload_time_iso_8601": "2020-12-23T01:07:44.900228Z", "url": "https://files.pythonhosted.org/packages/81/ee/e64e832019adde02563546cfcba85ca443bd230ba2655f17b6be964613d9/dfmpy-0.0.5.tar.gz", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "8f6911a920d4069e5c443f526a8c4e90", "sha256": "8c83e9e52e8186d1e4d75fca794620c173c433d1c29bbfd126c8c377eb46f96a" }, "downloads": -1, "filename": "dfmpy-0.0.5-py3-none-any.whl", "has_sig": false, "md5_digest": "8f6911a920d4069e5c443f526a8c4e90", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 22244, "upload_time": "2020-12-23T01:07:43", "upload_time_iso_8601": "2020-12-23T01:07:43.831048Z", "url": "https://files.pythonhosted.org/packages/18/ae/a8f4661b8f28ebdfa9bdd1a13ce84a67c706d191f89b3902424e5daa30cd/dfmpy-0.0.5-py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "5351957f5aa9cdaa572091a2bd7db786", "sha256": "4ef287de306ed407864e45f9b64e46ffbc644ca4cfcf96be0754e58b9e529504" }, "downloads": -1, "filename": "dfmpy-0.0.5.tar.gz", "has_sig": false, "md5_digest": "5351957f5aa9cdaa572091a2bd7db786", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 21881, "upload_time": "2020-12-23T01:07:44", "upload_time_iso_8601": "2020-12-23T01:07:44.900228Z", "url": "https://files.pythonhosted.org/packages/81/ee/e64e832019adde02563546cfcba85ca443bd230ba2655f17b6be964613d9/dfmpy-0.0.5.tar.gz", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }