pax_global_header00006660000000000000000000000064143643075120014517gustar00rootroot0000000000000052 comment=186487cee0778480572c2d887787ca3bf2da658f dotdrop-1.12.9/000077500000000000000000000000001436430751200132645ustar00rootroot00000000000000dotdrop-1.12.9/.coveragerc000066400000000000000000000000241436430751200154010ustar00rootroot00000000000000[run] parallel=True dotdrop-1.12.9/.gitattributes000066400000000000000000000000611436430751200161540ustar00rootroot00000000000000* linguist-vendored *.py linguist-vendored=false dotdrop-1.12.9/.github/000077500000000000000000000000001436430751200146245ustar00rootroot00000000000000dotdrop-1.12.9/.github/FUNDING.yml000066400000000000000000000000211436430751200164320ustar00rootroot00000000000000ko_fi: deadc0de6 dotdrop-1.12.9/.github/ISSUE_TEMPLATE/000077500000000000000000000000001436430751200170075ustar00rootroot00000000000000dotdrop-1.12.9/.github/ISSUE_TEMPLATE/bug_report.md000066400000000000000000000013431436430751200215020ustar00rootroot00000000000000--- name: Bug report about: report bug title: "[bug] " labels: bug assignees: '' --- Dotdrop version (and git commit if run from source): v0.xxx Using dotdrop: as a submodule, from pypi, '...' **Describe the bug** A clear and concise description of what the bug is. **Steps to Reproduce** Steps to reproduce the behavior: 1. Create a dotfile with content '...' 2. '...' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Additional information** The relevant part of the config file ```yaml config file content ``` Dotdrop's execution with the debug logs (`--verbose`) ```bash $ dotdrop --verbose ... ... ``` Any additional information that would help reproduce the bug. dotdrop-1.12.9/.github/ISSUE_TEMPLATE/feature_request.md000066400000000000000000000002651436430751200225370ustar00rootroot00000000000000--- name: Feature request about: Suggest an idea for this project title: "[feature]" labels: feature assignees: '' --- A clear and concise description of what you want to happen. dotdrop-1.12.9/.github/ISSUE_TEMPLATE/need-help.md000066400000000000000000000002231436430751200211670ustar00rootroot00000000000000--- name: Need help about: Get help title: "[help]" labels: help assignees: '' --- **What I am trying to achieve** **What I have tried so far** dotdrop-1.12.9/.github/workflows/000077500000000000000000000000001436430751200166615ustar00rootroot00000000000000dotdrop-1.12.9/.github/workflows/aur-release.yml000066400000000000000000000014571436430751200216200ustar00rootroot00000000000000name: Release to aur on: release: types: [created] workflow_dispatch: jobs: aur_publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Prepare AUR package env: RELEASE_TAG: ${{ github.ref }} run: | version=$(echo ${RELEASE_TAG} | sed 's#^.*v##g') sed -i "s/^pkgver=.*$/pkgver=${version}/g" packages/arch-dotdrop/PKGBUILD cat packages/arch-dotdrop/PKGBUILD - name: Publish to aur uses: KSXGitHub/github-actions-deploy-aur@v2.2.4 with: pkgname: dotdrop pkgbuild: ./packages/arch-dotdrop/PKGBUILD commit_username: ${{ secrets.AUR_USERNAME }} commit_email: ${{ secrets.AUR_EMAIL }} ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} commit_message: "bump version" dotdrop-1.12.9/.github/workflows/codeql-analysis.yml000066400000000000000000000052531436430751200225010ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "master" ] pull_request: # The branches below must be a subset of the branches above branches: [ "master" ] schedule: - cron: '27 3 * * 3' jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: [ 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v3 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v2 # â„šī¸ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 dotdrop-1.12.9/.github/workflows/homebrew-release.yml000066400000000000000000000004371436430751200226360ustar00rootroot00000000000000name: Release to Homebrew on: release: types: [created] workflow_dispatch: jobs: homebrew-core: runs-on: ubuntu-latest steps: - uses: dawidd6/action-homebrew-bump-formula@v3 with: token: ${{secrets.HOMEBREW_FORMULA_GH_TOKEN}} formula: dotdrop dotdrop-1.12.9/.github/workflows/pypi-release.yml000066400000000000000000000012601436430751200220020ustar00rootroot00000000000000name: Release to PyPI on: release: types: [created] workflow_dispatch: jobs: pypi_publish: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python 3.8 uses: actions/setup-python@v2 with: python-version: 3.8 - name: Install Tools run: | python -m pip install --upgrade pip pip install setuptools wheel twine if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - name: Build and Publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} run: | python setup.py sdist bdist_wheel twine upload dist/* dotdrop-1.12.9/.github/workflows/snapcraft-release.yml000066400000000000000000000007171436430751200230100ustar00rootroot00000000000000name: Release to Snapcraft on: release: types: [created] workflow_dispatch: jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: snapcore/action-build@v1 id: build with: path: packages - uses: snapcore/action-publish@v1 env: SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_LOGIN }} with: snap: ${{ steps.build.outputs.snap }} release: stable dotdrop-1.12.9/.github/workflows/testing.yml000066400000000000000000000022621436430751200210630ustar00rootroot00000000000000name: tests on: [push, pull_request, workflow_dispatch] jobs: test: runs-on: ubuntu-20.04 strategy: matrix: python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Setup Node uses: actions/setup-node@v3 with: node-version: 16 - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r tests-requirements.txt pip install -r requirements.txt npm install -g remark-cli remark-validate-links - name: Run with 1 worker run: | ./tests.sh env: DOTDROP_FORCE_NODEBUG: yes DOTDROP_NOBANNER: yes DOTDROP_WORKERS: 1 - name: Run with 4 workers run: | ./tests.sh env: DOTDROP_FORCE_NODEBUG: yes DOTDROP_NOBANNER: yes DOTDROP_WORKERS: 4 - name: Coveralls run: | pip install coveralls coveralls --service=github env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} dotdrop-1.12.9/.gitignore000066400000000000000000000003311436430751200152510ustar00rootroot00000000000000*.pyc __pycache__ .coverage .coverage* dist/ build/ *.egg-info/ tags env venv htmlcov # IDE .idea/ .vscode/ .atom/ # Snap packages parts/ prime/ stage/ *.snap /packages/snap/.snapcraft/ /packages/*_source.tar.bz2 dotdrop-1.12.9/CONTRIBUTING.md000066400000000000000000000163071436430751200155240ustar00rootroot00000000000000Contents * [Code base](#code-base) * [Config parsing](#config-parsing) * [Lower layer](#lower-layer) * [Higher layer](#higher-layer) * [Precedence](#precedence) * [Variable resolution](#variable-resolution) * [Rules](#rules) * [Testing](#testing) * [Testing with unittest](#testing-with-unittest) * [Testing with bash scripts](#testing-with-bash-scripts) * [Documentation](#documentation) Thanks for helping out! Feature requests, bug reports, and PRs are always welcome! This file provides a few pointers on how to contribute to dotdrop and where to find information. For any questions, feel free to open an issue. For PRs adding new features, I'd be very thankful if you could add either a unittest for the added feature or a bash script test (see [testing](#testing)), thanks! # Code base Dotdrop's code base is located in the [dotdrop directory](/dotdrop). Here's an overview of the different files and their roles: * **action.py**: represents the actions and transformations * **cfg_yaml.py**: the lower level config parser (see [lower layer](#lower-layer)) * **cfg_aggregator.py**: the higher level config parser (see [higher layer](#higher-layer)) * **comparator.py**: the class handling the comparison for `compare` * **dictparser.py**: abstract class for parsing dictionaries * **dotdrop.py**: the entry point and where the different CLI commands are executed * **dotfile.py**: represent a dotfile * **installer.py**: the class handling the installation of dotfile for `install` * **jhelpers.py**: list of methods available in templates with Jinja2 * **linktypes.py**: enum for the three types of linking (none, symlink, children) * **logger.py**: the custom logger * **options.py**: the class embedding all the different options across dotdrop * **profile.py**: represents a profile * **settings.py**: represents the config settings * **templategen.py**: the Jinja2 templating class * **updater.py**: the class handling the update of dotfiles for `update` * **utils.py**: some useful methods used across the code base # Config parsing The configuration file (YAML) is parsed using two layers: * First in the lower layer in [cfg_yaml.py](/dotdrop/cfg_yaml.py) * Then in the higher layer in [cfg_aggregator.py](/dotdrop/cfg_aggregator.py) Only the higher layer is accessible to other classes of dotdrop. ## Lower layer This is done in [cfg_yaml.py](/dotdrop/cfg_yaml.py). The lower layer part only takes care of basic types and does the following: * Normalize all config entries * Resolve paths (dotfiles src, dotpath, etc) * Refactor actions/transformations to a common format * Etc. * Import any data from external files (configs, variables, etc) * Apply variable substitutions * Complete any data if needed (add the "profile" variable, etc) * Execute intrepreted variables through the shell * Write new entries (dotfile, profile) into the dictionary and save it to a file * Fix any deprecated entries (link_by_default, etc) * Clear empty entries In the end, it builds a cleaned and normalized dictionary to be accessed by the higher layer. ## Higher layer This is done in [cfg_aggregator.py](/dotdrop/cfg_aggregator.py). The higher layer will transform the dictionary parsed by the lower layer into objects (profiles, dotfiles, actions, transformations, etc). The higher layer has no notion of inclusion (profile included, for example) or file importing (import actions etc.) or even interpreted variables (it only sees variables that have already been interpreted). It does the following: * Transform dictionaries into objects * Patch lists of keys with their corresponding objects (For example, dotfile's actions) * Provide getters for every dotdrop class that needs to access elements Note that any changes to the YAML dictionary (adding a new profile or a new dotfile for example) won't be *seen* by the higher layer until the config is reloaded. Consider the `dirty` flag as a sign the file needs to be written and its representation in higher levels in not accurate anymore. ## Precedence * `dynvariables` > `variables` * Profile `(dyn)variables` > any other `(dyn)variables` * Profile `(dyn)variables` > profile's included `(dyn)variables` * Imported `variables`/`dynvariables` > `(dyn)variables` ## Variable resolution How variables are resolved (through Jinja2's templating) in the config file. * Resolve main config file variables * Merge `variables` and `dynvariables` (allowing cyclic references) * Recursively template merged `variables` and `dynvariables` * `dynvariables` are executed * Profile's `variables` and `dynvariables` are merged * Resolve *included* entries (see below) * Paths and entries are templated (allows using something like `include {{@@ os @@}}.variables.yaml`) * *included* entries are processed * dyn-/variables are all resolved in their own file Potential *included* entries: * Entry *import_actions* * Entry *import_configs* * Entry *import_variables* * Profile's *import* * Profile's *include* Variables are then used to resolve different elements in the config file: see [this](docs/config/config-file.md#variables). ## Rules * `dynvariables` are executed in their own config file * Since `variables` and `dynvariables` are templated before the `dynvariables` are executed, this means that `dynvariables` can safely reference `variables`; however, `variables` referencing `dynvariables` will result in the *not-executed* value of the referenced `dynvariables` (see examples below). * Profiles cannot include profiles defined above in the import tree * Config files do not have access to variables defined above in the import tree * Actions/transformations using variables are resolved at runtime (when the action/transformation is executed) and not when loading the config * The same config file cannot be imported twice This will result in `dvar0 = "test"` and `var0 = "echo test"` (**not** `var0 = test`): ```yaml variables: var0: "{{@@ dvar0 @@}}" dynvariables: dvar0: "echo test" ``` This will result in `dvar0 = "test"` and `var0 = "test"`: ```yaml variables: var0: "test" dynvariables: dvar0: "echo {{@@ var0 @@}}" ``` # Testing Dotdrop is tested with the use of the [tests.sh](/tests.sh) script. * Test for PEP8 compliance with `pycodestyle` and `pyflakes` * Run the unittest available in [tests directory](/tests) * Run the bash script tests in [tests-ng directory](/tests-ng) ## Testing with unittest All unittests are available in [the tests directory](/tests) and use [Python's unittest](https://docs.python.org/3/library/unittest.html). The file [helpers.py](/tests/helpers.py) provides different helper methods for the tests. ## Testing with bash scripts The bash scripts are available in [tests-ng directory](/tests-ng). These test entire workflows by simulating the use of dotdrop from end to end for different use-cases (usually described in their filename or in the file header). Each script starts with the same boilerplate code that you can paste at the start of your new test (see the head of the file down to `# this is the test`). To run the tests on OSX, install following tools with homebrew ```bash brew install coreutils gnu-sed ``` # Documentation Dotdrop documentation is available under [https://dotdrop.readthedocs.io/](https://dotdrop.readthedocs.io/). dotdrop-1.12.9/LICENSE000066400000000000000000001045051436430751200142760ustar00rootroot00000000000000 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. {one line to give the program's name and a brief idea of what it does.} Copyright (C) {year} {name of author} 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: {project} Copyright (C) {year} {fullname} 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 . dotdrop-1.12.9/MANIFEST.in000066400000000000000000000000531436430751200150200ustar00rootroot00000000000000include README.md LICENSE requirements.txt dotdrop-1.12.9/README.md000066400000000000000000000247621436430751200145560ustar00rootroot00000000000000# dotdrop

[![GitHub release (latest by date)](https://img.shields.io/github/v/release/deadc0de6/dotdrop)](https://github.com/deadc0de6/dotdrop/releases/latest) [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](http://www.gnu.org/licenses/gpl-3.0) [![GitHub Repo stars](https://badgen.net/github/stars/deadc0de6/dotdrop)](https://github.com/deadc0de6/dotdrop/) [![Last commit](https://badgen.net/github/last-commit/deadc0de6/dotdrop)](https://github.com/deadc0de6/dotdrop/commits/master) [![Commits since last release](https://img.shields.io/github/commits-since/deadc0de6/dotdrop/latest)](https://github.com/deadc0de6/dotdrop/) [![Tests Status](https://github.com/deadc0de6/dotdrop/workflows/tests/badge.svg?branch=master)](https://github.com/deadc0de6/dotdrop/actions) [![Doc Status](https://readthedocs.org/projects/dotdrop/badge/?version=latest)](https://dotdrop.readthedocs.io/en/latest/?badge=latest) [![Coveralls](https://img.shields.io/coveralls/github/deadc0de6/dotdrop)](https://coveralls.io/github/deadc0de6/dotdrop?branch=master) ![CodeQL](https://github.com/deadc0de6/dotdrop/workflows/CodeQL/badge.svg) [![Python](https://img.shields.io/pypi/pyversions/dotdrop.svg)](https://pypi.python.org/pypi/dotdrop) [![PyPI](https://img.shields.io/pypi/v/dotdrop)](https://badge.fury.io/py/dotdrop) [![Homebrew version](https://img.shields.io/homebrew/v/dotdrop)](https://formulae.brew.sh/formula/dotdrop) [![AUR](https://img.shields.io/aur/version/dotdrop.svg)](https://aur.archlinux.org/packages/dotdrop) [![Snap](https://badgen.net/snapcraft/v/dotdrop)](https://snapcraft.io/dotdrop) [![Donate](https://img.shields.io/badge/donate-KoFi-blue.svg)](https://ko-fi.com/deadc0de6) *Save your dotfiles once, deploy them everywhere* [Dotdrop](https://github.com/deadc0de6/dotdrop) makes the management of dotfiles between different hosts easy. It allows you to store your dotfiles in Git and automagically deploy different versions of the same file on different setups. It also allows to manage different *sets* of dotfiles. For example, you can have a set of dotfiles for your home laptop and a different set for your office desktop. Those sets may overlap, and different versions of the same dotfiles can be deployed using different predefined *profiles*. Or you may have a main set of dotfiles for your everyday host and a subset you only need to deploy to temporary hosts (cloud VM etc.) that may be using a slightly different version of some of the dotfiles. Features: * Sync once every dotfile in Git for different usages * Allow dotfile templating * Dynamically generated dotfile contents with pre-defined variables * Comparison between deployed and stored dotfiles * Handling multiple profiles with different sets of dotfiles * Easily import and update dotfiles * Handle files and directories * Support symlinking of dotfiles * Associate actions to the deployment of specific dotfiles * Associate transformations for storing encrypted/compressed dotfiles * Provide solutions for handling dotfiles containing sensitive information Also check out the [blog post](https://deadc0de.re/articles/dotfiles.html), the [example](#getting-started), the [documentation](https://dotdrop.readthedocs.io/) or how [people are using dotdrop](https://dotdrop.readthedocs.io/en/latest/misc/people-using-dotdrop/) for more. Quick start: ```bash ## using dotdrop as a submodule mkdir dotfiles && cd dotfiles git init git submodule add https://github.com/deadc0de6/dotdrop.git pip3 install -r dotdrop/requirements.txt --user ./dotdrop/bootstrap.sh ./dotdrop.sh --help ``` A mirror of this repository is available on GitLab under . ## Why dotdrop? There exist many tools to manage dotfiles; however, not many allow to deploy different versions of the same dotfile on different hosts. Moreover, dotdrop allows to specify the set of dotfiles that need to be deployed for a specific profile. See the [example](#getting-started) for a concrete example of why [dotdrop](https://github.com/deadc0de6/dotdrop) rocks. --- **Table of Contents** * [Installation](#installation) * [Getting started](#getting-started) * [Documentation](#documentation) * [Thank you](#thank-you) # Installation See the [installation instructions](https://dotdrop.readthedocs.io/en/latest/installation/). Dotdrop is available on: * [PyPI](https://pypi.org/project/dotdrop/) * [Homebrew](https://formulae.brew.sh/formula/dotdrop) * [AUR (stable)](https://aur.archlinux.org/packages/dotdrop/) * [AUR (git version)](https://aur.archlinux.org/packages/dotdrop-git/) * [Snapcraft](https://snapcraft.io/dotdrop) * [Pacstall](https://github.com/pacstall/pacstall-programs/blob/master/packages/dotdrop/dotdrop.pacscript) # Getting started [Create a new repository](https://dotdrop.readthedocs.io/en/latest/getting-started/#repository-setup) to store your dotfiles with dotdrop. *Init* or *clone* that new repository and [install dotdrop](https://dotdrop.readthedocs.io/en/latest/installation/). Then import any dotfiles (files or directories) you want to manage with dotdrop. You can either use the default profile (which resolves to the *hostname* of the host you are running dotdrop on) or provide it explicitly using the switch `-p`/`--profile`. Import dotfiles on host *home*: ```bash $ dotdrop import ~/.vimrc ~/.xinitrc ~/.config/polybar ``` Dotdrop does two things: * Copy the dotfiles to the *dotpath* directory (defined in `config.yaml`, defaults to *dotfiles*) * Create the associated entries in the `config.yaml` file (in the `dotfiles` and `profiles` entries) Your config file will look like something similar to this: ```yaml config: backup: true banner: true create: true dotpath: dotfiles ignoreempty: false keepdot: false longkey: false showdiff: false workdir: ~/.config/dotdrop dotfiles: d_polybar: dst: ~/.config/polybar src: config/polybar f_vimrc: dst: ~/.vimrc src: vimrc f_xinitrc: dst: ~/.xinitrc src: xinitrc profiles: home: dotfiles: - f_vimrc - f_xinitrc - d_polybar ``` For a description of the different fields and their use, see the [config doc](https://dotdrop.readthedocs.io/en/latest/config/config-config/). Commit and push your changes with git. Then go to another host where your dotfiles need to be managed as well, clone the previously set up repository, and compare the local dotfiles with the ones stored in dotdrop: ```bash $ dotdrop compare --profile=home ``` Now you might want to adapt the `config.yaml` file to your liking on that second host. Let's say, for example, that you only want `d_polybar` and `f_xinitrc` to be deployed on that second host. You would then change your config to something like this (assuming that the second host's hostname is *office*): ```yaml â€Ļ profiles: home: dotfiles: - f_vimrc - f_xinitrc - d_polybar office: dotfiles: - f_xinitrc - d_polybar ``` Then adapt any dotfile using the [templating](https://dotdrop.readthedocs.io/en/latest/template/templating/) feature (if needed). For example, you might want different fonts sizes in Polybar for each host. Edit `/config/polybar/config`: ```bash â€Ļ {%@@ if profile == "home" @@%} font0 = sans:size=10;0 {%@@ elif profile == "office" @@%} font0 = sans:size=14;0 {%@@ endif @@%} font1 = "Material Design Icons:style=Regular:size=14;0" font2 = "unifont:size=6;0" â€Ļ ``` You also want to have the correct interface set on the wireless network in the Polybar config. Add a [variable](https://dotdrop.readthedocs.io/en/latest/config/config-variables/) to the config file (In the below example, *home* gets the default `wlan0` value for the variable `wifi` while *office* gets `wlp2s0`): ```yaml â€Ļ variables: wifi: "wlan0" â€Ļ profiles: home: dotfiles: - f_vimrc - f_xinitrc - d_polybar office: dotfiles: - f_xinitrc - d_polybar variables: wifi: "wlp2s0" ``` Then you can adapt the Polybar config file so that the variable `wifi` gets correctly replaced during installation: ```bash [module/wireless-network] type = internal/network interface = {{@@ wifi @@}} ``` Also, the home computer is running [awesomeWM](https://awesomewm.org/), and the office computer [bspwm](https://github.com/baskerville/bspwm). The `~/.xinitrc` file will therefore be different while still sharing some lines. Edit `/xinitrc`: ```bash #!/bin/bash # load Xresources userresources=$HOME/.Xresources if [ -f "$userresources" ]; then xrdb -merge "$userresources" & fi # launch the wm {%@@ if profile == "home" @@%} exec awesome {%@@ elif profile == "office" @@%} exec bspwm {%@@ endif @@%} ``` Finally you want everything installed with the *office* profile to be logged; you thus add an action to the config file: ```yaml â€Ļ actions: loginstall: "echo {{@@ _dotfile_abs_src @@}} installed to {{@@ _dotfile_abs_dst @@}} >> {0}" â€Ļ profiles: home: dotfiles: - f_vimrc - f_xinitrc - d_polybar office: dotfiles: - f_xinitrc - d_polybar variables: wifi: "wlp2s0" actions: - loginstall "/tmp/dotdrop-installation.log" ``` When done, you can install your dotfiles using: ```bash $ dotdrop install ``` If you are unsure, you can always run `dotdrop compare` to see how your local dotfiles would be updated by dotdrop before running `install` or you could run install with `--dry`. That's it, a single repository with all your dotfiles for your different hosts. For more, see the [docs](https://dotdrop.readthedocs.io): * [Create actions](https://dotdrop.readthedocs.io/en/latest/config/config-actions/) * [Use transformations](https://dotdrop.readthedocs.io/en/latest/config/config-transformations/) * [Use variables](https://dotdrop.readthedocs.io/en/latest/config/config-variables/) * [Symlink dotfiles](https://dotdrop.readthedocs.io/en/latest/howto/symlink-dotfiles/) * [and more](https://dotdrop.readthedocs.io/en/latest/howto/howto/) # Documentation Dotdrop's documentation is hosted on [Read the Docs](https://dotdrop.readthedocs.io/en/latest/). # Thank you If you like dotdrop, [buy me a coffee](https://ko-fi.com/deadc0de6). # Contribution If you are having trouble installing or using dotdrop, [open an issue](https://github.com/deadc0de6/dotdrop/issues). If you want to contribute, feel free to do a PR (please follow PEP8). Have a look at the [contribution guidelines](https://github.com/deadc0de6/dotdrop/blob/master/CONTRIBUTING.md). # License This project is licensed under the terms of the GPLv3 license. dotdrop-1.12.9/bootstrap.sh000077500000000000000000000005721436430751200156440ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 fold="dotfiles" conf="config.yaml" # copy dotdrop entry point cp dotdrop/dotdrop.sh dotdrop.sh chmod +x dotdrop.sh mkdir -p $fold if [ ! -e ${conf} ]; then # init config file cat << EOF > ${conf} config: backup: true create: true dotpath: $fold dotfiles: profiles: EOF fi dotdrop-1.12.9/completion/000077500000000000000000000000001436430751200154355ustar00rootroot00000000000000dotdrop-1.12.9/completion/README.md000066400000000000000000000016031436430751200167140ustar00rootroot00000000000000Here are completion files for `bash`, `zsh` and `fish` for the use of dotdrop through the bash script `dotdrop.sh` or through the Python script `dotdrop` (PyPI, snap, setup.py, etc.). `bash` and `zsh` scripts are generated using [infi.docopt_completion](https://github.com/Infinidat/infi.docopt_completion). # bash Source the file * if using `dotdrop.sh`: [dotdrop.sh-completion.bash](dotdrop.sh-completion.bash) * If using `dotdrop`: [dotdrop-completion.bash](dotdrop-completion.bash) # zsh Copy the file to a path within `${fpath}` * If using `dotdrop.sh`: [_dotdrop.sh-completion.zsh](_dotdrop.sh-completion.zsh) * If using `dotdrop`: [_dotdrop-completion.zsh](_dotdrop-completion.zsh) # fish Install for your user: ```bash mkdir -p ~/.config/fish/completions cp dotdrop.fish ~/.config/fish/completions/ ``` Install system-wide: ```bash cp dotdrop.fish /usr/share/fish/completions/ ``` dotdrop-1.12.9/completion/_dotdrop-completion.zsh000066400000000000000000000136071436430751200221530ustar00rootroot00000000000000#compdef dotdrop _message_next_arg() { argcount=0 for word in "${words[@][2,-1]}" do if [[ $word != -* ]] ; then ((argcount++)) fi done if [[ $argcount -le ${#myargs[@]} ]] ; then _message -r $myargs[$argcount] if [[ $myargs[$argcount] =~ ".*file.*" || $myargs[$argcount] =~ ".*path.*" ]] ; then _files fi fi } _dotdrop () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-h)-h[Show this screen.]' \ '(--help)--help[Show this screen.]' \ '(-v)-v[Show version.]' \ '(--version)--version[Show version.]' \ '*::options:->options' case $state in (command) local -a subcommands subcommands=( 'install[\[-VbtfndDa\] \[-c \] \[-p \] \[...\]]' 'import[\[-Vbdf\] \[-c \] \[-p \]]' 'compare[\[-Vb\] \[-c \] \[-p \]]' 'update[\[-VbfdkP\] \[-c \] \[-p \]]' 'remove[\[-Vbfdk\] \[-c \] \[-p \] \[...\]]' 'files[\[-VbTG\] \[-c \] \[-p \]]' 'detail[\[-Vb\] \[-c \] \[-p \] \[...\]]' 'profiles[\[-VbG\] \[-c \]]' ) _values 'dotdrop' $subcommands ;; (options) case $line[1] in install) _dotdrop-install ;; import) _dotdrop-import ;; compare) _dotdrop-compare ;; update) _dotdrop-update ;; remove) _dotdrop-remove ;; files) _dotdrop-files ;; detail) _dotdrop-detail ;; profiles) _dotdrop-profiles ;; esac ;; esac } _dotdrop-install () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-t)-t' \ '(--temp)--temp' \ '(-f)-f' \ '(--force)--force' \ '(-n)-n' \ '(--nodiff)--nodiff' \ '(-d)-d' \ '(--dry)--dry' \ '(-D)-D' \ '(--showdiff)--showdiff' \ '(-a)-a' \ '(--force-actions)--force-actions' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop-import () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-d)-d' \ '(--dry)--dry' \ '(-f)-f' \ '(--force)--force' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-l=-)-l=-' \ '(--link=-)--link=-' \ else myargs=('') _message_next_arg fi } _dotdrop-compare () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-C=-)-C=-' \ '(--file=-)--file=-' \ '(-i=-)-i=-' \ '(--ignore=-)--ignore=-' \ } _dotdrop-update () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-f)-f' \ '(--force)--force' \ '(-d)-d' \ '(--dry)--dry' \ '(-k)-k' \ '(--key)--key' \ '(-P)-P' \ '(--show-patch)--show-patch' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-i=-)-i=-' \ '(--ignore=-)--ignore=-' \ else myargs=('') _message_next_arg fi } _dotdrop-remove () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-f)-f' \ '(--force)--force' \ '(-d)-d' \ '(--dry)--dry' \ '(-k)-k' \ '(--key)--key' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop-files () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-T)-T' \ '(--template)--template' \ '(-G)-G' \ '(--grepable)--grepable' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ } _dotdrop-detail () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop-profiles () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-G)-G' \ '(--grepable)--grepable' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ } _dotdrop "$@"dotdrop-1.12.9/completion/_dotdrop.sh-completion.zsh000066400000000000000000000130741436430751200225620ustar00rootroot00000000000000#compdef dotdrop.sh _message_next_arg() { argcount=0 for word in "${words[@][2,-1]}" do if [[ $word != -* ]] ; then ((argcount++)) fi done if [[ $argcount -le ${#myargs[@]} ]] ; then _message -r $myargs[$argcount] if [[ $myargs[$argcount] =~ ".*file.*" || $myargs[$argcount] =~ ".*path.*" ]] ; then _files fi fi } _dotdrop.sh () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-h)-h[Show this screen.]' \ '(--help)--help[Show this screen.]' \ '(-v)-v[Show version.]' \ '(--version)--version[Show version.]' \ '*::options:->options' case $state in (command) local -a subcommands subcommands=( 'install' 'import' 'compare' 'update' 'remove' 'files' 'detail' 'profiles' ) _values 'dotdrop.sh' $subcommands ;; (options) case $line[1] in install) _dotdrop.sh-install ;; import) _dotdrop.sh-import ;; compare) _dotdrop.sh-compare ;; update) _dotdrop.sh-update ;; remove) _dotdrop.sh-remove ;; files) _dotdrop.sh-files ;; detail) _dotdrop.sh-detail ;; profiles) _dotdrop.sh-profiles ;; esac ;; esac } _dotdrop.sh-install () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-t)-t' \ '(--temp)--temp' \ '(-f)-f' \ '(--force)--force' \ '(-n)-n' \ '(--nodiff)--nodiff' \ '(-d)-d' \ '(--dry)--dry' \ '(-D)-D' \ '(--showdiff)--showdiff' \ '(-a)-a' \ '(--force-actions)--force-actions' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop.sh-import () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-d)-d' \ '(--dry)--dry' \ '(-f)-f' \ '(--force)--force' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-l=-)-l=-' \ '(--link=-)--link=-' \ else myargs=('') _message_next_arg fi } _dotdrop.sh-compare () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-C=-)-C=-' \ '(--file=-)--file=-' \ '(-i=-)-i=-' \ '(--ignore=-)--ignore=-' \ } _dotdrop.sh-update () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-f)-f' \ '(--force)--force' \ '(-d)-d' \ '(--dry)--dry' \ '(-k)-k' \ '(--key)--key' \ '(-P)-P' \ '(--show-patch)--show-patch' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ '(-i=-)-i=-' \ '(--ignore=-)--ignore=-' \ else myargs=('') _message_next_arg fi } _dotdrop.sh-remove () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-f)-f' \ '(--force)--force' \ '(-d)-d' \ '(--dry)--dry' \ '(-k)-k' \ '(--key)--key' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop.sh-files () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-T)-T' \ '(--template)--template' \ '(-G)-G' \ '(--grepable)--grepable' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ } _dotdrop.sh-detail () { local context state state_descr line typeset -A opt_args if [[ $words[$CURRENT] == -* ]] ; then _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ '(-p=-)-p=-' \ '(--profile=-)--profile=-' \ else myargs=('') _message_next_arg fi } _dotdrop.sh-profiles () { local context state state_descr line typeset -A opt_args _arguments -C \ ':command:->command' \ '(-V)-V' \ '(--verbose)--verbose' \ '(-b)-b' \ '(--no-banner)--no-banner' \ '(-G)-G' \ '(--grepable)--grepable' \ '(-c=-)-c=-' \ '(--cfg=-)--cfg=-' \ } _dotdrop.sh "$@"dotdrop-1.12.9/completion/dotdrop-completion.bash000066400000000000000000000054301436430751200221200ustar00rootroot00000000000000 _dotdrop() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update remove files detail profiles' -- $cur) ) else case ${COMP_WORDS[1]} in install) _dotdrop_install ;; import) _dotdrop_import ;; compare) _dotdrop_compare ;; update) _dotdrop_update ;; remove) _dotdrop_remove ;; files) _dotdrop_files ;; detail) _dotdrop_detail ;; profiles) _dotdrop_profiles ;; esac fi } _dotdrop_install() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-actions -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdrop_import() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -d --dry -f --force -c= --cfg= -p= --profile= -l= --link= ' -- $cur) ) fi } _dotdrop_compare() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -c= --cfg= -p= --profile= -C= --file= -i= --ignore= ' -- $cur) ) fi } _dotdrop_update() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -P --show-patch -c= --cfg= -p= --profile= -i= --ignore= ' -- $cur) ) fi } _dotdrop_remove() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdrop_files() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -T --template -G --grepable -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdrop_detail() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdrop_profiles() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -G --grepable -c= --cfg= ' -- $cur) ) fi } complete -o bashdefault -o default -o filenames -F _dotdrop dotdropdotdrop-1.12.9/completion/dotdrop.fish000066400000000000000000000114101436430751200177600ustar00rootroot00000000000000#!/usr/bin/env fish # All available subcommands # set commands\ install import compare update\ remove files detail profiles # Aliases to avoid walls of text # function __fish_dotdrop_comp_sub complete -f -c dotdrop\ -n "not __fish_seen_subcommand_from $commands"\ $argv end function __fish_dotdrop_list_profiles dotdrop profiles \ --grepable \ --no-banner \ 2> /dev/null end function __fish_dotdrop_list_test_keys -a "test_arg" # Return only dotdrop keys in which the src field matches a test(1) criteria # ^âŒŖ ^ ← "Slide Time!!" dotdrop files \ --grepable \ --no-banner \ 2> /dev/null | while read line # Single use variable because fish's output capture may change soon set -l dst (echo $line | cut -d, -f2 | cut -d: -f2 ) if test $test_arg "$dst" echo $line end end | cut -d, -f1 end # Complete subcommands # __fish_dotdrop_comp_sub -k -a "profiles" -d "List available profiles" __fish_dotdrop_comp_sub -k -a "detail" -d "List managed dotfiles details" __fish_dotdrop_comp_sub -k -a "files" -d "List managed dotfiles" __fish_dotdrop_comp_sub -k -a "remove" -d "Remove a managed dotfile" __fish_dotdrop_comp_sub -k -a "update" -d "Update a managed dotfile" __fish_dotdrop_comp_sub -k -a "compare" -d "Compare your local dotfiles with managed dotfiles" __fish_dotdrop_comp_sub -k -a "import" -d "Import dotfiles into dotdrop" __fish_dotdrop_comp_sub -k -a "install" -d "Install dotfiles" # Lone options # __fish_dotdrop_comp_sub -s h -l help __fish_dotdrop_comp_sub -s v -l version if test -z "$DOTDROP_PROFILE" set -l DOTDROP_PROFILE (uname -n) end # Short and Long options and their Descriptions # set -l a -s a -l force-actions -d "Execute all actions even if no dotfile is installed." set -l b -s b -l no-banner -d "Do not display the banner." set -l c -s c -l cfg -d "Path to the config." set -l C -s C -l file -d "Path of dotfile to compare." set -l d -s d -l dry -d "Dry run." set -l D -s D -l showdiff -d "Show a diff before overwriting." set -l f -s f -l force -d "Do not ask user confirmation for anything." set -l G -s G -l grepable -d "Grepable output." set -l i -s i -l ignore -d "Pattern to ignore." set -l k -s k -l key -d "Treat as a dotfile key." set -l l -s l -l link -d "Link option (nolink|absolute|relative|link_children)." set -l L -s L -l file-only -d "Do not show diff but only the files that differ." set -l m -s m -l preserve-mode -d "Insert a chmod entry in the dotfile with its mode." set -l n -s n -l nodiff -d "Do not diff when installing." set -l p -s p -l profile -d "Specify the profile to use [default: $DOTDROP_PROFILE]." set -l P -s P -l show-patch -d "Provide a one-liner to manually patch template." set -l s -s s -l as -d "Import as a different path from actual path." set -l t -s t -l temp -d "Install to a temporary directory for review." set -l T -s T -l template -d "Only template dotfiles." set -l V -s V -l verbose -d "Be verbose." set -l w -s w -l workers -d "Number of concurrent workers [default: 1]." set -l v -s v -l version -d "Show version." set -l h -s h -l help -d "Show this screen." # Configuration for arguments # set -al c -rF set -al C -rF set -al i -x set -al l -x -a "nolink absolute relative link_children" set -al p -x -a "(__fish_dotdrop_list_profiles)" set -al s -rF set -al w -x # Parameters for main commands # set -l mustfile -rF set -l nofile -F set -l nofile -F set -l files -d "File" -a "(__fish_dotdrop_list_test_keys -f)" set -l dirs -d "Directory" -a "(__fish_dotdrop_list_test_keys -d)" # Column 1 represents to what subcommands thes following options will be applied # Column 2 is a list which will be used for lookup from tables before for line in "install: V b t f n d D a c p w" \ "import: V b d f m c p s l" \ "compare: L V b c p w C i" \ "update: V b f d k P c p w i" \ "remove: V b f d k c p" \ "files: V b T G c p" \ "detail: V b c p" \ "profiles: V b G c" \ "install: files dirs" \ "detail: files dirs" \ "import: mustfile" \ "update: nofile" \ "remove: nofile" \ # I'm here so no one is hurt by sharp backslashes set -l command (echo "$line" | cut -d: -f1 ) for opt in (echo "$line" | cut -d: -f2 | string split -n ' ') complete -c dotdrop\ -n "__fish_seen_subcommand_from $command" $$opt end end # dotdrop.sh # complete -c dotdrop.sh -w dotdrop dotdrop-1.12.9/completion/dotdrop.sh-completion.bash000066400000000000000000000054771436430751200225440ustar00rootroot00000000000000 _dotdropsh() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -eq 1 ]; then COMPREPLY=( $( compgen -W '-h --help -v --version install import compare update remove files detail profiles' -- $cur) ) else case ${COMP_WORDS[1]} in install) _dotdropsh_install ;; import) _dotdropsh_import ;; compare) _dotdropsh_compare ;; update) _dotdropsh_update ;; remove) _dotdropsh_remove ;; files) _dotdropsh_files ;; detail) _dotdropsh_detail ;; profiles) _dotdropsh_profiles ;; esac fi } _dotdropsh_install() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -t --temp -f --force -n --nodiff -d --dry -D --showdiff -a --force-actions -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdropsh_import() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -d --dry -f --force -c= --cfg= -p= --profile= -l= --link= ' -- $cur) ) fi } _dotdropsh_compare() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -c= --cfg= -p= --profile= -C= --file= -i= --ignore= ' -- $cur) ) fi } _dotdropsh_update() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -P --show-patch -c= --cfg= -p= --profile= -i= --ignore= ' -- $cur) ) fi } _dotdropsh_remove() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -f --force -d --dry -k --key -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdropsh_files() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -T --template -G --grepable -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdropsh_detail() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -fW '-V --verbose -b --no-banner -c= --cfg= -p= --profile= ' -- $cur) ) fi } _dotdropsh_profiles() { local cur cur="${COMP_WORDS[COMP_CWORD]}" if [ $COMP_CWORD -ge 2 ]; then COMPREPLY=( $( compgen -W '-V --verbose -b --no-banner -G --grepable -c= --cfg= ' -- $cur) ) fi } complete -o bashdefault -o default -o filenames -F _dotdropsh dotdrop.shdotdrop-1.12.9/completion/generate.sh000077500000000000000000000023601436430751200175670ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # pip3 install --user infi.docopt_completion # set -e bin="docopt-completion" if ! hash ${bin}; then echo "\"${bin}\" not found!" exit 1 fi rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") opwd=$(pwd) # output files compl="${cur}" dtsh_zsh="${compl}/_dotdrop.sh-completion.zsh" dtsh_bash="${compl}/dotdrop.sh-completion.bash" dt_zsh="${compl}/_dotdrop-completion.zsh" dt_bash="${compl}/dotdrop-completion.bash" # generate for dotdrop.sh cd ${cur}/.. docopt-completion ./dotdrop.sh --manual-zsh mv ./_dotdrop.sh ${dtsh_zsh} docopt-completion ./dotdrop.sh --manual-bash mv ./dotdrop.sh.sh ${dtsh_bash} # generate for dotdrop vbin="virtualenv" if ! hash ${vbin}; then echo "\"${vbin}\" not found!" exit 1 fi cd ${cur}/.. venv="/tmp/dotdrop-venv" ${vbin} -p python3 ${venv} source ${venv}/bin/activate python setup.py install cd /tmp docopt-completion dotdrop --manual-zsh mv ./_dotdrop ${dt_zsh} docopt-completion dotdrop --manual-bash mv ./dotdrop.sh ${dt_bash} deactivate rm -rf ${venv} # pivot back cd ${opwd} dotdrop-1.12.9/config.toml000066400000000000000000000002411436430751200154230ustar00rootroot00000000000000[config] backup = true banner = true create = true dotpath = "dotfiles" keepdot = false link_dotfile_default = "nolink" link_on_import = "nolink" longkey = falsedotdrop-1.12.9/config.yaml000066400000000000000000000002671436430751200154220ustar00rootroot00000000000000config: backup: true banner: true create: true dotpath: dotfiles keepdot: false link_dotfile_default: nolink link_on_import: nolink longkey: false dotfiles: profiles: dotdrop-1.12.9/docs/000077500000000000000000000000001436430751200142145ustar00rootroot00000000000000dotdrop-1.12.9/docs/README.md000066400000000000000000000016411436430751200154750ustar00rootroot00000000000000# Dotdrop [Dotdrop](https://deadc0de.re/dotdrop/) is a dotfiles manager that provides efficient ways to manage your dotfiles. It is especially powerful when it comes to managing those across different hosts. The idea of dotdrop is to have the ability to store each dotfile only once and deploy them with different content on different hosts/setups. Most information on using dotdrop is described in this documentation and in the [readme](https://github.com/deadc0de6/dotdrop/blob/master/README.md). For more, check: * [A quick overview of dotdrop features](https://deadc0de.re/dotdrop/) * [The walkthrough example](https://github.com/deadc0de6/dotdrop#getting-started) * [The blogpost on dotdrop](https://deadc0de.re/articles/dotfiles.html) * [How people are using dotdrop](misc/people-using-dotdrop.md) For more config files examples, [search GitHub](https://github.com/search?q=filename%3Aconfig.yaml+dotdrop&type=Code). dotdrop-1.12.9/docs/config/000077500000000000000000000000001436430751200154615ustar00rootroot00000000000000dotdrop-1.12.9/docs/config/config-actions.md000066400000000000000000000051521436430751200207110ustar00rootroot00000000000000# Actions entry The **actions** entry (optional) contains an actions mapping. ```yaml actions: : ``` *pre* actions: ```yaml actions: pre: : ``` *post* actions: ```yaml actions: post: : ``` Actions can be either `post` or `pre`. * `post` action will be executed after the dotfile deployment. * `pre` action will be executed before the dotfile deployment. If you don't specify either `post` or `pre`, the action will be executed after the dotfile deployment (which is equivalent to `post`). Actions cannot obviously be named `pre` or `post`. Four types of actions can be defined: * [Dotfiles actions](config-dotfiles.md#dotfile-actions) * [Default actions](config-config.md#default_actions-entry) * [Profile actions](config-profiles.md#profile-actions-entry) * [Fake dotfiles and actions](config-actions.md#fake-dotfile-and-actions) **Notes**: * Any action with a key starting with an underscore (`_`) won't be shown in output. This can be useful when working with sensitive data containing passwords, for example. * Make sure to quote your actions to avoid bad surprises * Actions are executed using the default shell (`$SHELL`) * To use shell variables in your actions, you need to escape the curly brackets (`${HOME}` becomes `${{HOME}}`) ## Fake dotfile and actions *Fake* dotfile can be created by specifying no `dst` and no `src` (see [Fake dotfiles and actions](config-actions.md#fake-dotfile-and-actions)). By binding an action to such a *fake* dotfile, you make sure the action is always executed since *fake* dotfile are always considered installed. ```yaml actions: always_action: 'date > ~/.dotdrop.log' dotfiles: fake: src: dst: actions: - always_action ``` ## Dynamic actions Variables ([config variables and dynvariables](config-file.md#variables) and [template variables](../template/template-variables.md)) can be used in actions for more advanced use-cases. ```yaml dotfiles: f_test: dst: ~/.test src: test actions: - cookie_mv_somewhere "/tmp/moved-cookie" variables: cookie_dir_available: (test -d /tmp/cookiedir || mkdir -p /tmp/cookiedir) cookie_header: "{{@@ cookie_dir_available @@}} && echo 'header' > /tmp/cookiedir/cookie" cookie_mv: "{{@@ cookie_header @@}} && mv /tmp/cookiedir/cookie" actions: cookie_mv_somewhere: "{{@@ cookie_mv @@}} {0}" ``` or even something like this: ```yaml actions: log: "echo {0} >> {1}" config: default_actions: - preaction '{{@@ _dotfile_key @@}} installed' "/tmp/log" ... ``` Make sure to quote the actions using variables.dotdrop-1.12.9/docs/config/config-config.md000066400000000000000000000175361436430751200205270ustar00rootroot00000000000000# Config block The **config** entry (mandatory) contains global settings. Entry | Description | Default -------- | ------------- | ------------ `backup` | Create a backup of the dotfile in case it differs from the one that will be installed by dotdrop | true `banner` | Display the banner | true `check_version` | Check if a new version of dotdrop is available on github | false `chmod_on_import` | Always add a chmod entry on newly imported dotfiles (see `--preserve-mode`) | false `clear_workdir` | On `install` clear the `workdir` before installing dotfiles (see `--workdir-clear`) | false `compare_workdir` | On `compare` notify on files in `workdir` not tracked by dotdrop | false `cmpignore` | List of patterns to ignore when comparing, applied to all dotfiles (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) | - `create` | Create a directory hierarchy when installing dotfiles if it doesn't exist | true `default_actions` | List of action keys to execute for all installed dotfiles (See [actions](config-actions.md)) | - `diff_command` | The diff command to use for diffing files | `diff -r -u {0} {1}` `dotpath` | Path to the directory containing the dotfiles to be managed by dotdrop (absolute path or relative to the config file location) | `dotfiles` `filter_file` | List of paths to load templating filters from (See [Templating available filters](../template/template-filters.md)) | - `force_chmod` | If true, do not ask confirmation to apply permissions on install | false `func_file` | List of paths to load templating functions from (See [Templating available methods](../template/template-methods.md)) | - `ignore_missing_in_dotdrop` | Ignore missing files in dotdrop when comparing and importing (See [Ignore missing](config-file.md#ignore-missing)) | false `ignoreempty` | Do not deploy template if empty | false `impignore` | List of patterns to ignore when importing (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) | - `import_actions` | List of paths to load actions from (absolute path or relative to the config file location; see [Import actions from file](config-config.md#import_actions-entry)) | - `import_configs` | List of config file paths to be imported into the current config (absolute paths or relative to the current config file location; see [Import config files](config-config.md#import_configs-entry)) | - `import_variables` | List of paths to load variables from (absolute paths or relative to the config file location; see [Import variables from file](config-config.md#import_variables-entry)) | - `instignore` | List of patterns to ignore when installing, applied to all dotfiles (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) | - `keepdot` | Preserve leading dot when importing hidden file in the `dotpath` | false `key_prefix` | Prefix dotfile key on `import` with `f` for file and `d` for directory | true `key_separator` | Separator to use on dotfile key generation on `import` | `_` `link_dotfile_default` | Set a dotfile's `link` attribute to this value when undefined. Possible values: *nolink*, *absolute*, *relative* (See [Symlinking dotfiles](config-file.md#symlinking-dotfiles)) | `nolink` `link_on_import` | Set a dotfile's `link` attribute to this value when importing. Possible values: *nolink*, *absolute*, *relative* [Symlinking dotfiles](config-file.md#symlinking-dotfiles)) | `nolink` `longkey` | Use long keys for dotfiles when importing (See [Import dotfiles](../usage.md#import-dotfiles)) | false `minversion` | (*for internal use, do not modify*) Provides the minimal dotdrop version to use | - `showdiff` | On install, show a diff before asking to overwrite (See `--showdiff`) | false `template_dotfile_default` | Disable templating on all dotfiles when set to false | true `upignore` | List of patterns to ignore when updating, appled to all dotfiles (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) | - `workdir` | Path to the directory where templates are installed before being symlinked when using `link:absolute|relative|link_children` (absolute path or relative to the config file location) | `~/.config/dotdrop` link_by_default | When importing a dotfile, set `link` to this value by default | false ## import_variables entry It is possible to load variables/dynvariables from external files by providing their paths in the config entry `import_variables`. The paths can be absolute or relative to the config file location. `config.yaml` ```yaml config: backup: true create: true dotpath: dotfiles import_variables: - variables.d/myvars.yaml ``` `variables.d/myvars.yaml` ```yaml variables: var1: "extvar1" dynvariables: dvar1: "echo extdvar1" ``` Dotdrop will fail if an imported path points to a non-existing file. It is possible to make non-existing paths not fatal by appending `:optional` to the path: ```yaml import_variables: - variables.d/myvars.yaml:optional ``` ## import_actions entry It is possible to load actions from external files by providing their paths in the config entry `import_actions`. The paths can be absolute or relative to the config file location. `config.yaml` ```yaml config: backup: true create: true dotpath: dotfiles import_actions: - actions.d/myactions.yaml dotfiles: f_abc: dst: ~/.abc src: abc actions: - dateme ``` `actions.d/myactions.yaml` ```yaml actions: dateme: date > /tmp/timestamp ``` External/imported variables will take precedence over variables defined inside the main config file. Dotdrop will fail if an imported path points to a non-existing file. It is possible to make non-existing paths not fatal by appending `:optional` to the path: ```yaml import_actions: - actions.d/myactions.yaml:optional ``` ## import_configs entry Entire config files can be imported using the `import_configs` entry. This means making the following available from the imported config file in the original config file: * dotfiles * profiles * actions * read/write transformations * variables/dynvariables Paths to import can be absolute or relative to the importing config file location. `config.yaml` ```yaml config: backup: true create: true dotpath: dotfiles import_configs: - other-config.yaml dotfiles: f_abc: dst: ~/.abc src: abc actions: - show profiles: my-host: dotfiles: - f_abc - f_def my-haskell: include: - other-host ``` `other-config.yaml` ```yaml config: backup: true create: true dotpath: dotfiles-other import_actions: - actions.yaml dotfiles: f_def: dst: ~/.def src: def f_ghci: dst: ~/.ghci src: ghci profiles: other-host: dotfiles: - f_gchi ``` `actions.yaml` ```yaml actions: post: show: less ``` In this example, `config.yaml` imports `other-config.yaml`. The dotfile `f_def` used in the profile `my-host` is defined in `other-config.yaml`, and so is the profile `other-host` included from `my-haskell`. The action `show` is defined in `actions.yaml`, which is in turn imported by `other-config.yaml`. Dotdrop will fail if an imported path points to a non-existing file. It is possible to make non-existing paths not fatal by appending `:optional` to the path. ```yaml import_configs: - other-config.yaml:optional ``` ## default_actions entry Dotdrop allows to execute an action for any dotfile installation. These actions work as any other action (`pre` or `post`). For example, the below action will log each dotfile installation to a file. ```yaml actions: post: loginstall: "echo {{@@ _dotfile_abs_src @@}} installed to {{@@ _dotfile_abs_dst @@}} >> {0}" config: backup: true create: true dotpath: dotfiles default_actions: - loginstall "/tmp/dotdrop-installation.log" dotfiles: f_vimrc: dst: ~/.vimrc src: vimrc profiles: hostname: dotfiles: - f_vimrc ```dotdrop-1.12.9/docs/config/config-dotfiles.md000066400000000000000000000150711436430751200210630ustar00rootroot00000000000000# Dotfiles block The **dotfiles** entry (mandatory) contains a YAML object with sub-objects for the dotfiles managed by dotdrop. The entries in the sub-objects are as follows: Entry | Description -------- | ------------- `dst` | Where this dotfile needs to be deployed (dotfiles with empty `dst` are ignored and considered installed, can use `variables`, make sure to quote) `src` | Dotfile path within the `dotpath` (dotfiles with empty `src` are ignored and considered installed, can use `variables`, make sure to quote) `link` | Defines how this dotfile is installed. Possible values: *nolink*, *absolute*, *relative*, *link_children* (See [Symlinking dotfiles](config-file.md#symlinking-dotfiles)) (defaults to value of `link_dotfile_default`) `actions` | List of action keys that need to be defined in the **actions** entry below (See [actions](config-actions.md)) `chmod` | Defines the file permissions in octal notation to apply during installation or the special keyword `preserve` (See [permissions](config-file.md#permissions)) `cmpignore` | List of patterns to ignore when comparing (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) `ignore_missing_in_dotdrop` | Ignore missing files in dotdrop when comparing and importing (see [Ignore missing](config-file.md#ignore-missing)) `ignoreempty` | If true, an empty template will not be deployed (defaults to the value of `ignoreempty`) `instignore` | List of patterns to ignore when installing (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) `template` | If false, disable templating for this dotfile (defaults to the value of `template_dotfile_default`) `trans_read` | Transformation key to apply when installing this dotfile (must be defined in the **trans_read** entry below; see [transformations](config-transformations.md)) `trans_write` | Transformation key to apply when updating this dotfile (must be defined in the **trans_write** entry below; see [transformations](config-transformations.md)) `upignore` | List of patterns to ignore when updating (enclose in quotes when using wildcards; see [ignore patterns](config-file.md#ignore-patterns)) link_children | Replaced by `link: link_children` trans | Replaced by `trans_read` ```yaml : dst: src: ## Optional link: (nolink|absolute|relative|link_children) ignoreempty: (true|false) cmpignore: - "" upignore: - "" instignore: - "" actions: - template: (true|false) chmod: '' trans_read: trans_write: ``` ## Dotfile actions It is sometimes useful to execute some kind of action when deploying a dotfile. Note that a dotfile's actions are only executed when the dotfile is installed (that is, when the version present in dotdrop differs from the one in the filesystem). For example, let's consider [Vundle](https://github.com/VundleVim/Vundle.vim), used to manage Vim's plugins. The following action could be set to update and install the plugins when `vimrc` is deployed: ```yaml actions: vundle: vim +VundleClean! +VundleInstall +VundleInstall! +qall config: backup: true create: true dotpath: dotfiles dotfiles: f_vimrc: dst: ~/.vimrc src: vimrc actions: - vundle profiles: home: dotfiles: - f_vimrc ``` Thus, when `f_vimrc` is installed, the command `vim +VundleClean! +VundleInstall +VundleInstall! +qall` will be executed. Sometimes, you may even want to execute some action prior to deploying a dotfile. Let's take another example with [vim-plug](https://github.com/junegunn/vim-plug): ```yaml actions: pre: vim-plug-install: test -e ~/.vim/autoload/plug.vim || (mkdir -p ~/.vim/autoload; curl -fLo ~/.vim/autoload/plug.vim https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim) vim-plug: vim +PlugInstall +qall config: backup: true create: true dotpath: dotfiles dotfiles: f_vimrc: dst: ~/.vimrc src: vimrc actions: - vim-plug-install - vim-plug profiles: home: dotfiles: - f_vimrc ``` This way, we make sure [vim-plug](https://github.com/junegunn/vim-plug) is installed prior to deploying the `~/.vimrc` dotfile. You can also define `post` actions like this: ```yaml actions: post: some-action: echo "Hello, World!" >/tmp/log ``` Actions can even be parameterized. For example: ```yaml actions: echoaction: echo '{0}' > {1} config: backup: true create: true dotpath: dotfiles dotfiles: f_vimrc: dst: ~/.vimrc src: vimrc actions: - echoaction "vim installed" /tmp/mydotdrop.log f_xinitrc: dst: ~/.xinitrc src: xinitrc actions: - echoaction "xinitrc installed" /tmp/myotherlog.log profiles: home: dotfiles: - f_vimrc - f_xinitrc ``` The above will execute `echo 'vim installed' > /tmp/mydotdrop.log` when vimrc is installed and `echo 'xinitrc installed' > /tmp/myotherlog.log'` when xinitrc is installed. ## Dynamic dotfile paths Dotfile source (`src`) and destination (`dst`) paths can be dynamically constructed using defined variables ([variables and dynvariables](config-file.md#variables)). For example, to have a dotfile deployed on a unique Firefox profile where the profile path is dynamically found using a shell oneliner stored in a dynvariable: ```yaml dynvariables: mozpath: find ~/.mozilla/firefox -name '*.default' dotfiles: f_somefile: dst: "{{@@ mozpath @@}}/somefile" src: firefox/somefile profiles: home: dotfiles: - f_somefile ``` Or when you need to select the dotfile to deploy depending on the profile used (`ssh_config.perso` for the profile `perso` and `ssh_config.work` for the `work` profile): ```yaml dotfiles: f_ssh_config: src: "{{@@ ssh_config_file @@}}" dst: ~/.ssh/config profiles: perso: dotfiles: - f_ssh_config variables: - ssh_config_file: "ssh_config.perso" work: dotfiles: - f_ssh_config variables: - ssh_config_file: "ssh_config.work" ``` ## Dynamic dotfile link value Dotfile `link` values can be dynamically constructed using defined variables ([variables and dynvariables](config-file.md#variables)). For example: ```yaml variables: link_value: "nolink" dotfiles: f_test: src: test dst: ~/.test link: "{{@@ link_value @@}}" profiles: linux: dotfiles: - f_test variables: link_value: "absolute" windows: dotfiles: - f_test ``` Make sure to quote the link value in the config file.dotdrop-1.12.9/docs/config/config-dynvars.md000066400000000000000000000014431436430751200207360ustar00rootroot00000000000000# Dynvariables entry The **dynvariables** entry (optional) contains an interpreted variables mapping. ```yaml dynvariables: : ``` You can also use multi-line (see [yaml related doc](https://yaml-multiline.info/)). For example: ```yaml dynvariables: : >- ``` For example: ```yaml dynvariables: dvar1: head -1 /proc/meminfo dvar2: "echo 'this is some test' | rev | tr ' ' ','" dvar3: /tmp/my_shell_script.sh user: "echo $USER" config_file: >- test -f "{{@@ base_config @@}}" && echo "{{@@ base_config @@}}" || echo "{{@@ dfl_config @@}}" variables: base_config: "profile_base.yaml" dfl_config: "profile_default.yaml" ``` They have the same properties as [Variables](config-variables.md).dotdrop-1.12.9/docs/config/config-file.md000066400000000000000000000256331436430751200201760ustar00rootroot00000000000000# Config file ## Location The default config file used by dotdrop is [config.yaml](https://github.com/deadc0de6/dotdrop/blob/master/config.yaml). Unless specified otherwise, dotdrop will look in the following places for its config file and use the first one found: * Current/working directory or the directory where [dotdrop.sh](https://github.com/deadc0de6/dotdrop/blob/master/dotdrop.sh) is located if used * `${XDG_CONFIG_HOME}/dotdrop/` * `~/.config/dotdrop/` * `/etc/xdg/dotdrop/` * `/etc/dotdrop/` You can force dotdrop to use a different file either by using the `-c`/`--cfg` CLI switch or by defining the `DOTDROP_CONFIG` environment variable. ## Variables Multiple variables can be used within the config file to parametrize the following elements of the config: * Dotfile `src` and `dst` paths (See [Dynamic dotfile paths](config-dotfiles.md#dynamic-dotfile-paths)) * External paths * `import_variables` * `import_actions` * `import_configs` * Profiles' `import` * Profiles' `include` * `actions` * `transformations` Note that variables used in `actions` and `transformations` are resolved when the action/transformation is executed (See [Dynamic actions](config-actions.md#dynamic-actions), [Dynamic transformations](config-transformations.md#dynamic-transformations) and [Templating](../template/templating.md)). The following variables are available in the config files: * [Variables defined in the config](config-variables.md) * [Interpreted variables defined in the config](config-dynvars.md) * [User variables defined in the config](config-variables.md) * [Profile variables defined in the config](config-profiles.md#profile-variables-entry) * Environment variables: `{{@@ env['MY_VAR'] @@}}` * The [enriched variables](../template/template-variables.md#enriched-variables) * Dotdrop header: `{{@@ header() @@}}` (see [Dotdrop header](../template/templating.md#dotdrop-header)) as well as all [template methods](../template/template-methods.md) and [template filters](../template/template-filters.md). Note that all variables available in the config file will then be available during [templating](../template/templating.md). Here are some rules on the use of variables in configs: * [dynvariables](config-dynvars.md) are executed in their own file. * [dynvariables](config-dynvars.md) and [variables](config-variables.md) are templated before [dynvariables](config-dynvars.md) are executed. * Config files do not have access to variables defined above in the import tree (variables defined in importing config are not seen by the imported config file, where *import* can be any of `import_configs`, `import_variables`, `import_actions`, profile's `import` and profile's `include`) * [dynvariables](config-dynvars.md) take precedence over [variables](config-variables.md). * Profile `(dyn)variables` take precedence over any other `(dyn)variables`. * Profile `(dyn)variables` take precedence over profile's included `(dyn)variables`. * External/imported `(dyn)variables` take precedence over `(dyn)variables` defined inside the main config file. * [uservariables](config-uservars.md) are ignored if any other variable with the same key is defined. For more see the [CONTRIBUTING doc](/CONTRIBUTING.md). ## Permissions Dotdrop allows to control the permissions applied to a dotfile using the config dotfile entry [chmod](config-dotfiles.md). A [chmod](config-dotfiles.md) entry on a directory is applied to the directory only, not recursively. For example: ```yaml dotfiles: f_file: src: file dst: ~/file chmod: 644 f_dir: src: dir dst: ~/dir chmod: 744 f_preserve: src: preserve dst: ~/preserve chmod: preserve ``` The `chmod` value defines the file permissions in octal notation to apply on dotfiles. If undefined new files will get the system default permissions (see `umask`, `777-` for directories and `666-` for files). The special keyword `preserve` allows to ensure that if the dotfiles already exists on the filesystem, it is not altered during `install` and the `chmod` value won't be changed during `update`. On `import`, the following rules are applied: * If the `-m`/`--preserve-mode` switch is provided or the config option `chmod_on_import` is true, the imported file's permissions are stored in a `chmod` entry * If the imported file's permissions differ from the umask, then the permissions are automatically stored in the `chmod` entry. * Otherwise, no `chmod` entry is added On `install`, the following rules are applied: * If `chmod` is specified in the dotfile, it will be applied to the installed dotfile. * Otherwise, the permissions of the dotfile in the `dotpath` are applied. * If the global setting `force_chmod` is set to true, dotdrop will not ask for confirmation to apply permissions. * If `chmod` is `preserve` and the destination exists with a different permission set than system default, then it is not altered On `update`, the following rule is applied: * If the permissions of the file in the filesystem differ from the dotfile in the `dotpath`, then the dotfile entry `chmod` is added/updated accordingly (unless `chmod` value is `preserve`) ## Symlinking dotfiles see the [symlink dotfiles documentation](../howto/symlink-dotfiles.md). ## Template config entries Some entries in the config can be templated (See [templating](../template/templating.md)): Entry | Related doc -------- | ------------- dotpath | [config entries](config-config.md#config-block) dotfile src | [dynamic dotfile paths](config-dotfiles.md#dynamic-dotfile-paths) dotfile dst | [dynamic dotfile paths](config-dotfiles.md#dynamic-dotfile-paths) dotfile link | [dynamic dotfile link value](config-dotfiles.md#dynamic-dotfile-link-value) variables | [variables](config-variables.md) dynvariables | [dynvariables](config-dynvars.md) actions | [dynamic actions](config-dynvars.md) profile include | [Profile include](config-profiles.md#profile-include-entry) profile import | [Profile import](config-profiles.md#profile-import-entry) import_variables | [import_variables](config-config.md#import_variables-entry) import_actions | [import_actions](config-config.md#import_actions-entry) import_configs | [import_configs](config-config.md#import_configs-entry) ## All dotfiles for a profile To use all defined dotfiles in a profile, simply use the keyword `ALL`. For example: ```yaml dotfiles: f_xinitrc: dst: ~/.xinitrc src: xinitrc f_vimrc: dst: ~/.vimrc src: vimrc profiles: host1: dotfiles: - ALL host2: dotfiles: - f_vimrc ``` ## Ignore patterns It is possible to ignore specific patterns when using dotdrop. * For [install](../usage.md#install-dotfiles): * Using config block [instignore](config-config.md) * Using dotfiles block [instignore](config-dotfiles.md) * For [import](../usage.md#import-dotfiles): * Using config block [impignore](config-config.md) * For [compare](../usage.md#compare-dotfiles): * Using config block [cmpignore](config-config.md) * Using dotfiles block [cmpignore](config-dotfiles.md) * Using the command line switch `-i`/`--ignore` * For [update](../usage.md#update-dotfiles): * Using config block [upignore](config-config.md) * Using dotfiles block [upignore](config-dotfiles.md) * Using the command line switch `-i`/`--ignore` The ignore pattern must follow Unix shell-style wildcards, like, for example `*/path/to/file`. Make sure to quote these when using wildcards in the config file. ```yaml config: cmpignore: - '*/README.md' upignore: - '*/README.md' instignore: - '*/README.md' impignore: - '*/README.md' ... dotfiles: d_vim dst: ~/.vim src: vim upignore: - '*/undo-dir' - '*/plugged' instignore: - '*/internal' cmpignore: - '*/ignore-me' ... ``` Patterns used for a specific dotfile can be specified relative to the dotfile destination (`dst`). Similar to a `.gitignore` file, you can prefix ignore patterns with an exclamation point (`!`). This so-called "negative ignore pattern" will cause any files that match that pattern to __not__ be ignored, provided they *would have* been ignored by an earlier ignore pattern (dotdrop will warn if that is not the case). This feature allows to, for example, ignore all files within a certain directory, except for a particular one (See examples below). To completely ignore comparison of a specific dotfile: ```yaml dotfiles: d_vim dst: ~/.vim src: vim cmpignore: - '*' ``` To ignore a specific directory when updating: ```yaml dotfiles: d_colorpicker: src: config/some_directory dst: ~/.config/some_directory upignore: - '*sub_directory_to_ignore' ``` To ignore a specific file `testfile` and directory `testdir` when importing: ```yaml config: impignore: - "*/testfile" - "testdir" ``` To ignore all files within a certain directory relative to `dst`, except one called `custom_plugin.zsh`: ```yaml dotfiles: d_zsh: src: zsh dst: ~/.config/zsh upignore: - "plugins/*" - "!plugins/custom_plugin.zsh" ``` To ignore everything except a single file named `file`: ```yaml dotfiles: d_dir src: dir dst: ~/dir cmpignore: - '!file' - '[a-zA-Z0-9]*' ``` To ignore specific files on different profiles (same `src` but some files are not installed for specific profile) ```yaml dotfiles: d_testdir_p1: src: testdir dst: ~/.testdir instignore: - '*/ignore-me-1' d_testdir_p2: src: testdir dst: ~/.testdir instignore: - '*/ignore-me-2' profiles: p1: dotfiles: - d_testdir_p1 p2: dotfiles: - d_testdir_p2 ``` ## Ignore missing Sometimes, it is nice to have [update](../usage.md#update-dotfiles) not copy all the files in the installed directory or [compare](../usage.md#compare-dotfiles) diff them. For example, maybe you only want to include a single configuration file in your repository and don't want to include other files the program uses, such as a cached files. Maybe you only want to change one file and don't want the others cluttering your repository. Maybe the program changes these files quite often and creates unnecessary diffs in your dotfiles. In these cases, you can use the [ignore-missing](config-config.md) option. This option is available as a flag (`--ignore-missing` or `-z`) to the `update` and `compare` commands, or [as ignore-missing in the config](config-config.md). To configure globally, place the following in `config.yaml`: ```yaml config: ignore_missing_in_dotdrop: true ``` To configure per dotfile: ```yaml dotfiles: f_abc: ignore_missing_in_dotdrop: true ``` ## toml Dotdrop should be able to handle `toml` config file however this feature hasn't been extensively tested. A base [config.toml](/config.toml) is available to get started. The script [yaml-to-toml.py](https://github.com/deadc0de6/dotdrop/blob/master/scripts/yaml-to-toml.py) allows to convert a `yaml` dotdrop config file to `toml`. For more see issue [#343](https://github.com/deadc0de6/dotdrop/issues/343).dotdrop-1.12.9/docs/config/config-profiles.md000066400000000000000000000075401436430751200210770ustar00rootroot00000000000000# Profiles entry The **profiles** entry (mandatory) contains a YAML object with sub-objects for the profiles for the different dotfiles that need to be managed. The entries in the sub-objects are as follows: Entry | Description -------- | ------------- `dotfiles` | The dotfiles associated with this profile `import` | List of paths containing dotfile keys for this profile (absolute path or relative to the config file location; see [Import profile dotfiles from file](config-profiles.md#profile-import-entry)). `include` | Include all elements (dotfiles, actions, (dyn)variables, etc) from another profile (See [Include dotfiles from another profile](config-profiles.md#profile-include-entry) and [meta profiles](../howto/group-hosts.md)) `variables` | Profile-specific variables (See [Variables](config-file.md#variables)) `dynvariables` | Profile-specific interpreted variables (See [Interpreted variables](config-dynvars.md)) `actions` | List of action keys that need to be defined in the **actions** entry below (See [actions](config-actions.md)) ```yaml : dotfiles: - - - ... ## Optional include: - - ... variables: : dynvariables: : actions: - - ... import: - - ... ``` ## Profile include entry If one profile is using the entire set of another profile, one can use the `include` entry to avoid redundancy. Note that everything from the included profile is made available (actions, variables/dynvariables, etc). See also an example in [meta profiles](../howto/group-hosts.md). For example: ```yaml profiles: host1: dotfiles: - f_xinitrc include: - host2 host2: dotfiles: - f_vimrc ``` Here profile *host1* contains all the dotfiles defined for *host2* plus `f_xinitrc`. For more advanced use-cases, variables ([variables](config-variables.md) and [dynvariables](config-dynvars.md)) can be used to specify the profile to include in a profile: For example: ```yaml variables: var1: "john" dynvariables: d_user: "echo $USER" profiles: profile_john: dotfiles: - f_john_dotfile profile_bill: dotfiles: - f_bill_dotfile p1: include: - "profile_{{@@ d_user @@}}" p2: include: - "profile_{{@@ var1 @@}}" ``` Note that profiles cannot include other profiles defined above in the import tree (for example, when a profile exists in another file and is imported using `import_configs`). ## Profile import entry A profile's dotfiles list can be loaded from external files by specifying their paths in the config entry `import` under the specific profile. The paths can be absolute or relative to the config file location. `config.yaml` ```yaml dotfiles: f_abc: dst: ~/.abc src: abc f_def: dst: ~/.def src: def f_xyz: dst: ~/.xyz src: xyz profiles: p1: dotfiles: - f_abc import: - somedotfiles.yaml ``` `somedotfiles.yaml` ``` dotfiles: - f_def - f_xyz ``` Variables can be used in `import`, what allows to do something like: ```yaml import: - profiles.d/{{@@ profile @@}}.yaml ``` ## Profile variables entry Profile variables will take precedence over globally defined variables. This means that you could do something like this: ```yaml variables: git_email: home@email.com dotfiles: f_gitconfig: dst: ~/.gitconfig src: gitconfig profiles: work: dotfiles: - f_gitconfig variables: git_email: work@email.com private: dotfiles: - f_gitconfig ``` ## Profile actions entry A profile action can be either a `pre` or `post` action (see [actions](config-actions.md)). These are executed before any dotfile installation (for `pre`) and after all dotfile installations (for `post`) only if at least one dotfile has been installed.dotdrop-1.12.9/docs/config/config-transformations.md000066400000000000000000000067501436430751200225070ustar00rootroot00000000000000# Transformations entry For examples of transformation uses, see: * [Handle compressed directories](../howto/store-compressed-directories.md) * [Handle secrets](../howto/sensitive-dotfiles.md) **Notes**: * Any transformation with a key starting with an underscore (`_`) won't be shown in output. This can be useful when working with sensitive data containing passwords, for example. * Make sure to quote your transformations to avoid bad surprises * Transformations are executed using the default shell (`$SHELL`) * To use shell variables in your transformations you need to escape the curly brackets (`${HOME}` becomes `${{HOME}}`) There are two types of transformations available: * **Read transformations**: used to transform dotfiles before they are installed ([config](config-config.md) key `trans_read`) * Used for commands `install` and `compare` * They have two mandatory arguments: * **{0}** will be replaced with the dotfile to process * **{1}** will be replaced with a temporary file to store the result of the transformation * This Happens **before** the dotfile is templated (see [templating](../template/templating.md)) * **Write transformations**: used to transform files before updating a dotfile ([config](config-config.md) key `trans_write`) * Used for command `update` and `import` * They have two mandatory arguments: * **{0}** will be replaced with the file path to update the dotfile with * **{1}** will be replaced with a temporary file to store the result of the transformation A typical use-case for transformations is when dotfiles need to be stored encrypted or compressed. For more, see [the howto](../howto/howto.md). Note that transformations cannot be used if the dotfile is to be linked (when `link: absolute|relative|link_children`). Transformations also support additional positional arguments that must start from 2 (since `{0}` and `{1}` are added automatically). The transformations themselves as well as their arguments can also be templated. For example: ```yaml trans_read: targ: echo "$(basename {0}); {{@@ _dotfile_key @@}}; {2}; {3}" > {1} dotfiles: f_abc: dst: /tmp/abc src: abc trans_read: targ "{{@@ profile @@}}" lastarg profiles: p1: dotfiles: - f_abc ``` will result in `abc; f_abc; p1; lastarg`. ## trans_read entry The **trans_read** entry (optional) contains a transformations mapping (See [transformations](config-transformations.md)). ```yaml trans_read: : ``` ## trans_write entry The **trans_write** entry (optional) contains a write transformations mapping (See [transformations](config-transformations.md)). ```yaml trans_write: : ``` ## Dynamic transformations As for [dynamic actions](config-actions.md#dynamic-actions), transformations support the use of variables ([variables and dynvariables](config-file.md#variables) and [template variables](../template/template-variables.md#template-variables)). A very dumb example: ```yaml trans_read: r_echo_abs_src: echo "{0}: {{@@ _dotfile_abs_src @@}}" > {1} r_echo_var: echo "{0}: {{@@ r_var @@}}" > {1} trans_write: w_echo_key: echo "{0}: {{@@ _dotfile_key @@}}" > {1} w_echo_var: echo "{0}: {{@@ w_var @@}}" > {1} variables: r_var: readvar w_var: writevar dotfiles: f_abc: dst: ${tmpd}/abc src: abc trans_read: r_echo_abs_src trans_write: w_echo_key f_def: dst: ${tmpd}/def src: def trans_read: r_echo_var trans_write: w_echo_var ``` dotdrop-1.12.9/docs/config/config-uservars.md000066400000000000000000000022251436430751200211210ustar00rootroot00000000000000# Uservariables entry The **uservariables** entry (optional) contains a collection of variables whose values are queried from the user (See [User variables](config-variables.md)). ```yaml uservariables: : ``` If you want to manually enter variables' values, you can use the `uservariables` entry. Each variable will be prompted to the user. For example: ```yaml uservariables: emailvar: "email" ``` will prompt the user to enter a value for the variable `emailvar`: ``` Please provide the value for "email": ``` And store the entered text as the value for the variable `email`. The variable can then be used as any other [variable](config-file.md#variables). `uservariables` are eventually saved to `uservariables.yaml` (relatively to the config file). This allows to use the following construct to prompt once for some specific variables and then store them in a file. You might also want to add `uservariables.yaml` to your `.gitignore`. ```yaml uservariables: emailvar: "email" config: import_variables: - uservariables.yaml:optional ``` For an example, see [prompt user for variables](../howto/prompt-user-for-variables.md). dotdrop-1.12.9/docs/config/config-variables.md000066400000000000000000000017161436430751200212230ustar00rootroot00000000000000# Variables entry The **variables** entry (optional) contains a variables mapping (See [variables](config-file.md#variables)). ```yaml variables: : ``` Variables defined in the `variables` entry are made available within the config file. For example ```yaml variables: myvar: "some value" home: "{{@@ env['HOME'] @@}}" email: "user@domain.com" ``` Config variables are recursively evaluated, which means that a config like the below: ```yaml variables: var1: "var1" var2: "{{@@ var1 @@}} var2" var3: "{{@@ var2 @@}} var3" var4: "{{@@ dvar4 @@}}" dynvariables: dvar1: "echo dvar1" dvar2: "{{@@ dvar1 @@}} dvar2" dvar3: "{{@@ dvar2 @@}} dvar3" dvar4: "echo {{@@ var3 @@}}" ``` will result in the following available variables: * var1: `var1` * var2: `var1 var2` * var3: `var1 var2 var3` * var4: `echo var1 var2 var3` * dvar1: `dvar1` * dvar2: `dvar1 dvar2` * dvar3: `dvar1 dvar2 dvar3` * dvar4: `var1 var2 var3`dotdrop-1.12.9/docs/css/000077500000000000000000000000001436430751200150045ustar00rootroot00000000000000dotdrop-1.12.9/docs/css/extra.css000066400000000000000000000020031436430751200166340ustar00rootroot00000000000000/* * from https://github.com/AaltoSciComp/scicomp-docs/blob/master/_static/theme_overrides.css */ /* override table width restrictions: https://github.com/rtfd/sphinx_rtd_theme/issues/117 */ .wy-table-responsive table td, .wy-table-responsive table th { white-space: normal !important; } @media screen and (min-width: 767px) { .wy-table-responsive { max-width: 100% !important; overflow: visible !important; } } /* RST has an extra space after sub-lists in unordered lists. This */ /* fixes that annoying problem. */ .rst-content .section ul p { margin-bottom: 0px; } .rst-content .section li ol { margin-bottom: 0px; } /* Strikethrough */ /* https://stackoverflow.com/questions/6518788/rest-strikethrough */ .strike { text-decoration: line-through; } /* */ .wy-side-nav-search > a img.logo, .wy-side-nav-search .wy-dropdown > a img.logo { max-width: 70%; } details summary { font-size: large; font-weight: bold; margin-bottom:12px } details { margin-bottom:20px } dotdrop-1.12.9/docs/getting-started.md000066400000000000000000000040321436430751200176420ustar00rootroot00000000000000# Getting started ## Repository setup Either create a Git repository on your prefered platform and clone it or create one locally. This repository will contain two main elements, dotdrop's config file (`config.yaml`) and a directory containing all your dotfiles managed by dotdrop. ```bash ## clone your repository (my-dotfiles) $ git clone /my-dotfiles $ cd my-dotfiles ## within the repository create a directory to store your dotfiles ## (refered by "dotpath" in the config, which defaults to "dotfiles") $ mkdir dotfiles ``` Then add a config file. You can get a [minimal config file](https://github.com/deadc0de6/dotdrop/blob/master/config.yaml) from dotdrop's repository with: ```bash $ wget https://raw.githubusercontent.com/deadc0de6/dotdrop/master/config.yaml ``` It is recommended to store your config file directly within your repository (*my-dotfiles* in the example above), but you could save it in different places if you wish; see [config location](config/config-file.md#location) for more. ```bash $ tree my-dotfiles my-dotfiles ├── config.yaml └── dotfiles ``` If your config file is in an exotic location, you can add an alias in your preferred shell to call dotdrop with the config file path argument. ``` alias dotdrop='dotdrop --cfg=' ``` For more info on the config file format, see [the config file doc](config/config-file.md). ## Basic usage The basic use of dotdrop is: * Import a file/directory to manage (this will copy the files from the filesystem to your `dotpath`): `dotdrop import ` * Install the dotfiles (this will *copy/link* them from your `dotpath` to the filesystem): `dotdrop install` Then if you happen to update the file/directory directly on the filesystem (add a new file/dir, edit content, etc.) you can use the `update` command to mirror back those changes in dotdrop. For more advanced uses: * `dotdrop --help` for the CLI usage. * [The usage doc](usage.md) * [The example](https://github.com/deadc0de6/dotdrop#getting-started) * [The howto](howto/howto.md)dotdrop-1.12.9/docs/howto/000077500000000000000000000000001436430751200153545ustar00rootroot00000000000000dotdrop-1.12.9/docs/howto/append.md000066400000000000000000000021621436430751200171460ustar00rootroot00000000000000# Append text on install Sometimes it might be useful to be able to append some text to a file. Dotdrop is able to do that with the help of [actions](../config/config-actions.md) and a temporary file. Below is a config example to append to a file: ```yaml dynvariables: tmpfile: mktemp variables: somefile_final: ~/.somefile dotfiles: f_somefile: dst: "{{@@ tmpfile @@}}" src: somefile actions: - strip "{{@@ somefile_final @@}}" - append "{{@@ tmpfile @@}}" "{{@@ somefile_final @@}}" actions: pre: strip: "sed -i '/^# my pattern$/,$d' {0}" post: append: "cat {0} >> {1}; rm -f {0}" ``` During installation, the `strip` action is executed before the installation, and it strips everything from the pattern `# my pattern` to the end of the file. Then the dotfile `somefile` is installed in a temporary location (here `tmpfile`) and finally the post action `append` will append the contents of the `tmpfile` to the final dotfile pointed to by `somefile_final`. Obviously, the dotfile in the dotpath should start with a unique pattern (here `# my pattern`): ``` # my pattern this is the end ``` dotdrop-1.12.9/docs/howto/create-special-files.md000066400000000000000000000030311436430751200216540ustar00rootroot00000000000000# Create files on install One way to create symlinks (or any other special file) is to use a combination of [actions](../config/config-actions.md) and a *fake* dotfile. Let's say, for example, you have a list of directories you want to link from under `~/.original` to `~/symlinks`. ```bash $ tree ~/.original /home/user/.original ├── dir1 ├── dir2 └── dir3 ``` First you would store these directory names in a text file in your `/links.txt`: ``` dir1 dir2 dir3 ``` The config file would contain different elements: * A `dynvariables` that will read the above text file * A few `variables` for the source and destination * An action that will create the destination directory and symlink those directories * A *fake* dotfile (with no `src` and no `dst` values) that will be always installed with the above action ```yaml dynvariables: links_list: "cat {{@@ _dotdrop_dotpath @@}}/links.txt | xargs" ... variables: links_dst: "{{@@ env['HOME'] @@}}/.symlinks" links_src: "{{@@ env['HOME'] @@}}/.original" ... actions: symlink_them: 'mkdir -p "{1}" && for lnk in {0}; do ln -s "{{@@ links_src @@}}/$lnk" "{1}/$lnk"; done' ... fake: src: dst: actions: - symlink_them '{{@@ links_list @@}}' '{{@@ links_dst @@}}' ``` The result would be: ```bash $ tree ~/.symlinks /home/user/.symlinks ├── dir1 -> /home/user/.original/dir1 ├── dir2 -> /home/user/.original/dir2 └── dir3 -> /home/user/.original/dir3 ``` For reference, see [issue 243](https://github.com/deadc0de6/dotdrop/issues/243). dotdrop-1.12.9/docs/howto/group-hosts.md000066400000000000000000000034371436430751200201770ustar00rootroot00000000000000# Group hosts in config and meta profiles Let's consider the situation where you have multiple hosts from different distros and you want an easy way to structure your config file nicely but also simplify the use of templates (since multiple hosts in the same distro would share the same configs parts - or if branch in templates). You define two types of profiles: * **Meta profiles**: for example for distros it would be something like `os_arch`, `os_debian` and so on. These are never directly used for installing dotfiles but instead included by other profiles. * **Host profiles** (defaults to hostnames): the usual `home`, `office`, etc Each *Host profile* would include a *meta profile* and inherit all its dotfiles as well as it variables. For example in the *meta profile* you would define variables like `distro: debian` that you could use in your templates with `{%@@ if distro == "debian" @@%}` to target all profiles that inherit from the same *meta profile*. ```yaml profiles: meta_base: dotfiles: - f_zshrc - f_zshrc os_arch: variables: distro: arch include: - meta-base os_debian: variables: distro: debian include: - meta-base home: include: - os_arch dotfiles: - f_vimrc office: include: - os_debian dotfiles: - f_something ``` You then have the opportunity in your templates to do the following that would select the if branch for all profiles inheriting from a specific *meta profile*. ``` # zsh-syntax-highlighting # https://github.com/zsh-users/zsh-syntax-highlighting {%@@ if distro == "arch" @@%} source /usr/share/zsh/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh {%@@ elif distro == "debian" @@%} source /usr/share/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh {%@@ endif @@%} ```dotdrop-1.12.9/docs/howto/howto.md000066400000000000000000000017471436430751200170470ustar00rootroot00000000000000# How To ## Append text to a dotfile on install [Append text to a dotfile on install](append.md) ## Create files on install [Create files on install](create-special-files.md) ## Handle compressed directories [Store compressed directories](store-compressed-directories.md) ## Handle secrets [Handle secrets](sensitive-dotfiles.md) ## Handle special chars [Handle special chars](special-chars.md) ## Improve Git integration [Improve Git integration](improve-git-integration.md) ## Include files or templates in templates [Include files or templates in templates](include-in-template.md) ## Manage system dotfiles [Manage system dotfiles](system-config-files.md) ## Merge files on install [Merge files on install](merge-files-when-installing.md) ## Prompt user for variables [Prompt user for variables](prompt-user-for-variables.md) ## Share content across dotfiles [Share content across dotfiles](sharing-content.md) ## Symlink dotfiles [Symlink dotfiles](symlink-dotfiles.md) dotdrop-1.12.9/docs/howto/improve-git-integration.md000066400000000000000000000010141436430751200224550ustar00rootroot00000000000000# Improve Git integration The below aliases can help with the process of updating your dotfiles between multiple hosts. Add them to your `~/.zshrc` or `~/.bashrc`. You can then simply run `dotsync` to push or pull from your dotfile repository. ``` # Your dotdrop git repository location export DOTREPO="/path/to/your/dotdrop/repo" alias dotdrop="$DOTREPO/dotdrop.sh" alias dotgit="git -C $DOTREPO" alias dotsync="dotgit pull && dotgit add -A && dotgit commit && dotgit push; dotdrop install" ``` Provided by ReekyMarko. dotdrop-1.12.9/docs/howto/include-in-template.md000066400000000000000000000010721436430751200215360ustar00rootroot00000000000000# Include files or templates in templates [Jinja2](https://jinja.palletsprojects.com/en/2.11.x/templates/) provides the ability to include an external file/template from within a template with the directive `include`. See the [related docs](https://jinja.palletsprojects.com/en/2.11.x/templates/#include) for more. The path must be relative to the `dotpath`. For example: ```yaml {%@@ include 'colors/black.colors' @@%} ``` Of course, paths could be also dynamically generated using variables. For example: ```yaml {%@@ include colors_path + '/black.colors' @@%} ``` dotdrop-1.12.9/docs/howto/merge-files-when-installing.md000066400000000000000000000037261436430751200232060ustar00rootroot00000000000000# Merge files on install Dotdrop allows to merge multiple files into one using Jinja2's `include` directive. For example, let's assume you want to keep your `.vimrc` split into multiple parts in dotdrop: * `/vimrc.d/top`: top part of the file * `/vimrc.d/bottom`: bottom part of the file And you want dotdrop to merge all those files into `~/.vimrc` whenever you process your .vimrc with dotdrop. First make sure `~/.vimrc` is present in your config file: ```yaml ... dotfiles: f_vimrc: dst: ~/.vimrc src: vimrc profiles: hostname: dotfiles: - f_vimrc ... ``` Note that the subfiles (`vimrc.d/top` and `vimrc.d/bottom`) are not known to the config and do not need to be. Edit the stored vimrc file to include the other files, for example: ```bash $ cat /vimrc {%@@ include 'vimrc.d/top' @@%} filetype on set t_Co=256 set tw=0 set tabstop=2 set shiftwidth=2 set expandtab set nocompatible set nomodeline syntax on {%@@ include 'vimrc.d/bottom' @@%} ``` The `include` path parameter needs to be relative to your `dotpath`. Dotdrop will then automagically include the files into your vimrc when handling `f_vimrc`. ## Merge all files in a directory To include all files in a directory, a combination of [dynvariables](../config/config-dynvars.md) and [Jinja2 directives](https://jinja.palletsprojects.com/en/2.11.x/) have to be used. Let's say all files in `/toinclude` need to be included into a dotfile. First define a [dynvariables](../config/config-dynvars.md) in the config file which will look for files to include in the above directory: ```yaml dynvariables: allfiles: "cd {{@@ _dotdrop_dotpath @@}}; find toinclude/ -type f | xargs" ``` Note that `_dotdrop_dotpath` is part of the built-in variables (For more, see [template variables](../template/template-variables.md#template-variables)). Then use the generated list in the dotfile template: ``` {%@@ for f in allfiles.split() @@%} {%@@ include f @@%} {%@@ endfor @@%} ``` dotdrop-1.12.9/docs/howto/prompt-user-for-variables.md000066400000000000000000000016361436430751200227330ustar00rootroot00000000000000# Prompt user for variables With the use of [uservariables](../config/config-uservars.md), one can define specific variables that need to be initially filled in manually by the user on first run. The provided values are then automatically saved by dotdrop to `uservariables.yaml`, which can be included in the main config as a file from which variables are imported using [import_variables](../config/config-config.md). Let's say, for example, that you want to manually provide the email value on new hosts you deploy your dotfiles to. You'd add the following elements to your config: ```yaml uservariables: emailvar: "email" config: import_variables: - uservariables.yaml:optional ``` On first run, the `emailvar` is prompted to the user and then saved to `uservariables.yaml`. Since this file is imported, the value for `emailvar` will automatically be filled in without prompting the user on subsequent calls. dotdrop-1.12.9/docs/howto/sensitive-dotfiles.md000066400000000000000000000071501436430751200215210ustar00rootroot00000000000000# Handle secrets * [Using environment variables](#using-environment-variables) * [Store encrypted dotfiles using GPG](#store-encrypted-dotfiles-using-gpg) * [GPG examples](#gpg-examples) ## Using environment variables For example, you can have an `.env` file in the directory where your `config.yaml` lies: ```bash ## Some secrets pass="verysecurepassword" ``` If this file contains secrets that should not be tracked by Git, put it in your `.gitignore`. You can then invoke dotdrop with the help of an alias ```bash # when dotdrop is installed as a submodule alias dotdrop='eval $(grep -v "^#" ~/dotfiles/.env) ~/dotfiles/dotdrop.sh' # when dotdrop is installed from package alias dotdrop='eval $(grep -v "^#" ~/dotfiles/.env) /usr/bin/dotdrop --cfg=~/dotfiles/config.yaml' ``` The above aliases load all the variables from `~/dotfiles/.env` (while omitting lines starting with `#`) before calling dotdrop. Defined variables can then be used [in the config](../config/config-file.md#template-config-entries) or [for templating dotfiles](../template/templating.md) For more see [the doc on environment variables](../template/template-variables.md#environment-variables). ## Store encrypted dotfiles using GPG First you need to define the encryption/decryption methods, for example ```yaml variables: keyid: "11223344" trans_read: _decrypt: "gpg -q --for-your-eyes-only--no-tty -d {0} > {1}" trans_write: _encrypt: "gpg -q -r {{@@ keyid @@}} --armor --no-tty -o {1} -e {0}" ``` You can then import your dotfile and specify the transformations to apply/associate. ```bash dotdrop import --transw=_encrypt --transr=_decrypt ~/.secret ``` Now whenever you install/compare your dotfile, the `_decrypt` transformation will be executed to get the clear version of the file. When updating the `_encrypt` transformation will transform the file to store it encrypted. See [transformations](../config/config-transformations.md). ## gpg examples Using GPG keys: ```yaml variables: keyid: "11223344" trans_read: _decrypt: "gpg -q --for-your-eyes-only--no-tty -d {0} > {1}" trans_write: _encrypt: "gpg -q -r {{@@ keyid @@}} --armor --no-tty -o {1} -e {0}" ``` Passphrase is stored in a environement variable: ```yaml trans_read: _decrypt: "echo {{@@ env['THE_KEY'] @@}} | gpg -q --batch --yes --for-your-eyes-only --passphrase-fd 0 --no-tty -d {0} > {1}" trans_write: _encrypt: "echo {{@@ env['THE_KEY'] @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -o {1} -c {0}" ``` Passphrase is stored as a variable: ```yaml variables: gpg_password: "some password" trans_read: _decrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --for-your-eyes-only --passphrase-fd 0 --no-tty -d {0} > {1}" trans_write: _encrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -o {1} -c {0}" ``` Passphrase is retrieved using a script: ```yaml dynvariables: gpg_password: "./get-password.sh" trans_read: _decrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --for-your-eyes-only --passphrase-fd 0 --no-tty -d {0} > {1}" trans_write: _encrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -o {1} -c {0}" ``` Passphrase is stored in a file: ```yaml variables: gpg_password_file: "/tmp/the-password" dynvariables: gpg_password: "cat {{@@ gpg_password_file @@}}" trans_read: _decrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --for-your-eyes-only --passphrase-fd 0 --no-tty -d {0} > {1}" trans_write: _encrypt: "echo {{@@ gpg_password @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -o {1} -c {0}" ``` See also [transformations](../config/config-transformations.md).dotdrop-1.12.9/docs/howto/sharing-content.md000066400000000000000000000137751436430751200210160ustar00rootroot00000000000000# Share content across dotfiles There are cases in which two or more dotfiles are very similar. For example, two files exporting environment variables for two projects built with the same technology (eg. two node.js web servers deployed on AWS). In these cases it's nice to share as much code as possible across the dotfiles by leveraging templating and merging them into the same dotfile in dotdrop's `dotpath`. Here are a few suggestions about how to achieve this: * [Brute force templating](#brute-force-templating) * [Profile variables](#profile-variables) * [Jinja macros](#jinja-macros) ## Brute force templating The first approach is sheer use of templating and variables. In order to do this, we need to: 1. Create the merged dotfile with an arbitrary name somewhere in `dotpath`. 2. Create two `dotfile` entries in `config.yaml`, both having the merged dotfile as `src`, but their own `dst`. Here we have an example: The merged dotfile in `dotpath` (`dotpath/projects/env`): ```bash # .env {%@@ if _dotfile_key == 'server0-env' @@%} {%@@set aws_db_host = 'super-duper.host' @@%} {%@@set aws_db_port = 4521 @@%} {%@@ elif _dotfile_key == 'server1-env' @@%} {%@@set aws_db_host = 'cheaper.host' @@%} {%@@set aws_db_host = 9632 @@%} {%@@ endif @@%} export DB_HOST='{{@@ aws_db_host @@}}' export DB_PORT='{{@@ aws_db_port @@}}' ``` Part of dotdrop `config.yaml` file: ```yaml # config.yaml dotfiles: server0-env: src: projects/env dst: ~/projects/server0/.env server1-env: src: projects/env dst: ~/projects/server1/.env ``` Installing the dotfile `server0-env` will create an environment file in `~/projects/server0/.env` with the following content: ```bash # .env export DB_HOST='super-duper.host' export DB_PORT='4521' ``` ## Profile variables The previous method, albeit flexible, is a bit cumbersome for some use cases. For example, when the dotfiles belong to different profiles, the cleanest solution consists of using [profile variables](../config/config-profiles.md#profile-variables-entry). This is achieved by: 1. Creating the merged dotfile with an arbitrary name somewhere in `dotpath`. 2. Adding some variables in the merged dotfile via templating. 3. Overriding them with different values in each profile via profile variables. 4. Typically, making the dotfile `dst` dynamic, as different profiles need usually to deploy the dotfiles in different locations. **NOTE**: This technique does *not* require two different `dotfiles` entry in `config.yaml`. An example: The merged dotfile (`dotpath/projects/env`): ```bash # .env export DB_HOST='{{@@ aws_db_host @@}}' export DB_PORT='{{@@ aws_db_port @@}}' ``` Part of dotdrop `config.yaml` file: ```yaml # config.yaml dotfiles: env: src: projects/env dst: '{{@@ server_path @@}}/.env' profiles: server0: dotfiles: - env variables: aws_db_host: super-duper.host aws_db_port: 4521 server_path: ~/projects/server0 server1: dotfiles: - env variables: aws_db_host: cheaper.host aws_db_port: 9632 server_path: ~/projects/server1 ``` With this setup, installing the `server1` profile will create an environment file in `~/projects/server1/.env` with the following content: ```bash # .env export DB_HOST='cheaper.host' export DB_PORT='9632' ``` ## Jinja macros Even though it has cleaner dotfiles, the profile-variable-based procedure can't be used in two scenarios: when the dotfiles belong to the same profile, and when variable values require some complex computations. In both cases, the brute force templating approach can be used, but in the latter one it also makes the dotfiles bloated with "bookkeeping" logic, thus hard to read. A solution for this relies in leveraging Jinja macros. This method is a variation of the brute force templating one where the merged dotfile is included from many different dotfiles in `dotpath` via Jinja macros rather than via many `dotfile` entries with the same `src` attribute. This way, the merged original dotfiles stays clean as in the profile variables solution because computations are in other files. The steps to achieve this are: 1. Creating the merged dotfile with an arbitrary name somewhere in `dotpath`. 2. Wrapping the whole content of the merged dotfile in a Jinja macro with the necessary parameters. 3. Calling the macro in each original dotfile, computing the parameters there. **NOTE**: The merged dotfile will be empty, as it only contains a Jinja macro. If it needs to not be deployed, the `ignoreempty` entry can be set to `true` in `config.yaml`. As usual, an example: The merged dotfile in `dotpath` (`dotpath/projects/env`): ```bash {%@@ macro env(db_host, db_port) @@%} # .env export DB_HOST='{{@@ db_host @@}}' export DB_PORT='{{@@ db_port @@}}' {%@@ endmacro @@%} ``` Server0's environment file (`projects/server0/.env`): ```jinja2 {%@@ from projects/env import env @@%} {%@@ set keyPieces = _dotfile_key.split('-') @@%} {%@@ if keyPieces[-1] == 'dbg' @@%} {%@@ set isLocal = keyPieces[-2] == 'local' @@%} {%@@ set remote_host = 'super-duper-dbg.host' if not isLocal else 'localhost' @@%} {%@@set aws_db_port = 3333 @@%} {%@@ elif keyPieces[-1] == 'dev' @@%} {%@@set aws_db_host = 'localhost' @@%} {%@@set aws_db_host = 4521 @@%} {%@@ endif @@%} {{@@ env(db_host, db_port) @@}} ``` Server1's environment file (`projects/server1/.env`): ```jinja2 {%@@ from projects/env import env @@%} {{@@ env('average-host.com', 9632) @@}} ``` Part of dotdrop `config.yaml` file: ```yaml # config.yaml dotfiles: server0-env-remote-dbg: src: projects/server0/.env dst: ~/projects/server0/.env.remote.dbg server0-env-local-dbg: src: projects/server0/.env dst: ~/projects/server0/.env.local.dbg server1-env: src: projects/server1/.env dst: ~/projects/server1/.env ``` With this configuration, installing the dotfile `server0-env-local-dbg` will create an environment file in `~/projects/server0/.env.local.dbg` with the following content: ```bash # .env export DB_HOST='localhost' export DB_PORT='3333' ``` dotdrop-1.12.9/docs/howto/special-chars.md000066400000000000000000000031701436430751200204150ustar00rootroot00000000000000# Handle special chars * [Detect encoding](#detect-encoding) * [Special chars](#special-chars) * [Re-encode](#re-encode) --- ## Detect encoding Text file encoding can be identified using, for example, `file -b ` or in vim with `:set fileencoding`. Here's an example of encoding that will fully work with dotdrop: ```bash $ file -b UTF-8 Unicode text, with escape sequences ``` and another that will mislead the `compare` command and return false/inaccurate results: ```bash $ file -b ISO-8859 text, with escape sequences ``` ## Special chars ### CRLF The use of dotfiles with DOS/Windows line endings (CRLF, `\r\n`) will result in the comparison (`compare`) returning a difference where there is none. This is due to Jinja2 stripping CRLF. One solution is to use `dos2unix` to re-format the dotfiles before adding them to dotdrop. See . ### Non-Unicode chars Jinja2 is not able to process non-Unicode chars (). This means that dotfiles using non-Unicode chars can still be fully managed by dotdrop; however, when comparing the local file with the one stored in dotdrop, `compare` will return a difference even if there is none. Either replace the non-Unicode chars (see below [Re-encode](#re-encode)) or accept the fact the comparison shows a difference while there's none. See . ## Re-encode To change an existing file's encoding, you can use `recode UTF-8 ` (see [recode](https://linux.die.net/man/1/recode)) or in vim `:set fileencoding=utf-8`. dotdrop-1.12.9/docs/howto/store-compressed-directories.md000066400000000000000000000017121436430751200235070ustar00rootroot00000000000000# Handle compressed directories This is an example of how to use transformations (`trans_read` and `trans_write`) to store compressed directories and deploy them with dotdrop. Start by defining the transformations: ```yaml trans_read: uncompress: "mkdir -p {1} && tar -xf {0} -C {1}" trans_write: compress: "tar -cf {1} -C {0} ." ``` Then import the directory by specifying which transformations to apply/associate: ```bash dotdrop import --transw=compress --transr=uncompress ~/.somedir ``` The *read* transformation `uncompress` is used to execute the below command before installing/comparing the dotfile (where `{0}` is the source and `{1}` the destination): ```bash mkdir -p {1} && tar -xf {0} -C {1} ``` And the *write* transformation `compress` is run when updating the dotfile directory by compressing it (where `{0}` is the source and `{1}` the destination): ```bash tar -cf {1} -C {0} . ``` See [transformations](../config/config-transformations.md).dotdrop-1.12.9/docs/howto/symlink-dotfiles.md000066400000000000000000000110051436430751200211700ustar00rootroot00000000000000# Symlink dotfiles Dotdrop is able to install dotfiles in four different ways, which are controlled by the `link` config attribute of each dotfile: * `link: nolink`: The dotfile (file or directory) is copied to its destination * `link: absolute`: The dotfile (file or directory) is linked to its destination using an absolute symlink * `link: relative`: The dotfile (file or directory) is linked to its destination using a relative symlink * `link: link_children`: The direct children of the dotfile (directory only) are symlinked to their destination. For every direct child of `src`, symlink `dst/` to `src/` (See [Link children](#link-children)) Note that if the dotfile uses template directives, it will first be installed to your `workdir` (defaults to `~/.config/dotdrop`) and then symlinked (see [Templating symlinked dotfiles](#templating-symlinked-dotfiles)). Although the config entries `link_on_import` and `link_dotfile_default` can be set to the value `link_children`, it is not recommended, since operations on a dotfile that is not a directory with the option `link_children` will fail. ## Symlink a dotfile Below is an ad-hoc way to symlink a dotfile when [link_dotfile_default](https://dotdrop.readthedocs.io/en/latest/config/config-config/) and [link_on_import](https://dotdrop.readthedocs.io/en/latest/config/config-config/) use their default values. Import the file: ```bash $ dotdrop import ~/.bashrc -> "/home/user/.bashrc" imported ``` Edit the `config.yaml` and set the `link` value to `absolute`: ```yaml dotfiles: f_bashrc: src: bashrc dst: ~/.bashrc link: absolute ``` Install the dotfile, which will remove your `~/.bashrc` and replace it with a link to the file stored in dotdrop: ```bash $ dotdrop install Remove "/home/user/.bashrc" for link creation? [y/N] ? y -> linked /home/user/.bashrc to /home/user/dotdrop/dotfiles/bashrc 1 dotfile(s) installed. ``` The dotfile then points to the file in dotdrop: ```bash $ readlink ~/.bashrc /home/user/dotdrop/dotfiles/bashrc ``` ## Link children The `link_children` option can be very useful for dotfiles when you don't want the entire directory to be symlinked but still want to keep a clean config file (with a limited number of entries). Note that `link_children` can only be applied to directories. *Make sure to do a backup of your dotfiles with something like `cp -r {,.bak}`.* A good example of its use is when managing `~/.vim` with dotdrop. First let's see what it looks like when using `link: absolute`: ```yaml config: dotpath: dotfiles dotfiles: vim: dst: ~/.vim src: vim link: absolute ``` The top directory `~/.vim` is symlinked to the `/vim` location: ```bash $ readlink ~/.vim ~/.dotfiles/vim/ $ ls ~/.dotfiles/vim/ after autoload plugged plugin snippets spell swap vimrc ``` As a result, all files under `~/.vim` will be managed by dotdrop (including unwanted directories like `spell`, `swap`, etc.). Now with `link_children` dotdrop allows to only symlink direct children of the dotfile directory. Let's say only `after`, `plugin`, `snippets`, and `vimrc` need to be managed in dotdrop. `~/.vim` is imported in dotdrop and cleaned of all unwanted files/directories, and then the `link` entry is set to `link_children` in the config file: ```yaml config: dotpath: dotfiles dotfiles: vim: dst: ~/.vim/ src: vim link: link_children ``` Now all children of the `vim` dotfile's directory in the *dotpath* will be symlinked under `~/.vim/` without affecting the rest of the local files, keeping the config file clean and all unwanted files only on the local system. ```bash $ readlink -f ~/.vim ~/.vim $ tree -L 1 ~/.vim ~/.vim ├── after -> ~/.dotfiles/vim/after ├── autoload ├── plugged ├── plugin -> ~/.dotfiles/vim/plugin ├── snippets -> ~/.dotfiles/vim/snippets ├── spell ├── swap └── vimrc -> ~/.dotfiles/vim/vimrc ``` ## Templating symlinked dotfiles Dotfiles not using any templating directives are directly linked to dotdrop's `dotpath` directory (see [the config file doc](../config/config-file.md)). When using templating directives, however, the dotfiles are first installed into `workdir` (defaults to *~/.config/dotdrop*; see [the doc](../config/config-config.md)) and then symlinked there. This applies to both dotfiles with `link: absolute|relative` and `link: link_children`. For example: ```bash # with template /home/user/.xyz -> /home/user/.config/dotdrop/.xyz # without template /home/user/.xyz -> /home/user/dotfiles/xyz ``` dotdrop-1.12.9/docs/howto/system-config-files.md000066400000000000000000000023661436430751200215740ustar00rootroot00000000000000# Manage system dotfiles Dotdrop doesn't allow to handle file owernership (at least not directly). Every file operation (create/copy file/directory, create symlinks, etc.) is executed with the rights of the user calling dotdrop. Using dotdrop with `sudo` to manage unprivileged and privileged files in the same *session* is a bad idea as the resulting files will all have messed-up owners. It is therefore recommended to have two different config files (and thus two different *dotpath*s) for handling these two uses cases: For example: * One `config-user.yaml` for the local/user dotfiles (with its dedicated *dotpath*, for example `dotfiles-user`) * One `config-root.yaml` for the system/root dotfiles (with its dedicated *dotpath*, for example `dotfiles-root`) `config-user.yaml` is used when managing the user's dotfiles: ```bash ## user config file is config-user.yaml $ dotdrop import --cfg config-user.yaml $ dotdrop install --cfg config-user.yaml ... ``` `config-root.yaml` is used when managing the system's dotfiles and is to be used with `sudo` or directly by the root user: ```bash ## root config file is config-root.yaml $ sudo dotdrop import --cfg=config-root.yaml $ sudo dotdrop install --cfg=config-root.yaml ... ``` dotdrop-1.12.9/docs/installation.md000066400000000000000000000152311436430751200172410ustar00rootroot00000000000000# Installation * [As a submodule](#as-a-submodule) * [PyPI package](#pypi-package) * [Homebrew package](#homebrew-package) * [Debian unstable (sid)](#debian) * [Ubuntu lunar (23.04)](#ubuntu) * [AUR packages](#aur-packages) * [Snap package](#snap-package) * [From source](#from-source) * [Pacstall package](https://github.com/pacstall/pacstall-programs/blob/master/packages/dotdrop/dotdrop.pacscript) ## As a submodule Having dotdrop as a submodule guarantees that anywhere you are cloning your dotfiles Git tree from you will have dotdrop shipped with it. Note that when using dotdrop as a submodule you will be tracking the master branch (and not a specific version) The following will create a Git repository for your dotfiles and keep dotdrop as a submodule. ```bash ## create the repository $ mkdir dotfiles; cd dotfiles $ git init ## install dotdrop as a submodule $ git submodule add https://github.com/deadc0de6/dotdrop.git $ pip3 install --user -r dotdrop/requirements.txt $ ./dotdrop/bootstrap.sh ## use dotdrop $ ./dotdrop.sh --help ``` For macOS users, make sure to install `realpath` through Homebrew (part of *coreutils*) and `libmagic`. Using dotdrop as a submodule will require you to work with dotdrop by using the generated script `dotdrop.sh` at the root of your dotfiles repository. Note that this script updates the submodule automatically unless called with the environment variable `DOTDROP_AUTOUPDATE` set to `no`. If you happened to encounter `ModuleNotFoundError` error after an update, it means the dependencies have changed and you should re-install dependencies with ```bash pip3 install --user -r dotdrop/requirements.txt ``` To ease the use of dotdrop, it is recommended to add an alias to it in your shell with the config file path; for example: ``` alias dotdrop= --cfg=' ``` ### As a submodule in a virtualenv To install it in a [virtualenv](https://virtualenv.pypa.io): ```bash ## create the repository $ mkdir dotfiles; cd dotfiles $ git init ## install dotdrop as a submodule $ git submodule add https://github.com/deadc0de6/dotdrop.git $ virtualenv -p python3 env $ echo 'env' > .gitignore $ source env/bin/activate $ pip install -r dotdrop/requirements.txt $ ./dotdrop/bootstrap.sh ## use dotdrop $ ./dotdrop.sh --help ``` When using a virtualenv, make sure to source the environment before using dotdrop: ```bash $ source env/bin/activate $ ./dotdrop.sh --help ``` Then follow the instructions under [As a submodule](#as-a-submodule). ### Upgrade dotdrop submodule If using dotdrop as a submodule, one can control if dotdrop is auto-updated through the [dotdrop.sh](https://github.com/deadc0de6/dotdrop/blob/master/dotdrop.sh) script by defining the environment variable `DOTDROP_AUTOUPDATE=yes`. If undefined, `DOTDROP_AUTOUPDATE` will take the value `yes`. If used as a submodule, update it with: ```bash $ git submodule update --init --recursive $ git submodule update --remote dotdrop ## install dependencies $ pip3 install --user -r dotdrop/requirements.txt ``` You will then need to commit the changes with: ```bash $ git add dotdrop $ git commit -m 'update dotdrop' $ git push ``` ### Downgrade dotdrop submodule If you wish to get a specific version of dotdrop when using it as a submodule, the following operations can be done. Here dotdrop is downgraded to the latest stable version: ```bash ## enter the repository containing the dotdrop submodule $ cd my-dotfiles ## enter the dotdrop submodule $ cd dotdrop ## update the list of tags $ git fetch --tags ## checkout the latest stable version $ git checkout `git tag -l | tail -1` ``` If using the `dotdrop.sh` script, make sure it doesn't automatically update dotdrop back to the latest commit. ## PyPI package [PyPI package](https://pypi.org/project/dotdrop/) Install dotdrop: ```bash $ pip3 install dotdrop --user ``` ### PyPI package in a virtualenv Install dotdrop from PyPI in a virtualenv: ```bash $ virtualenv -p python3 env $ source env/bin/activate $ pip install dotdrop ``` When using a virtualenv, make sure to source the environment before using dotdrop: ```bash $ source env/bin/activate $ dotdrop --help ``` Then follow the instructions under [PyPI package](#pypi-package). ## Homebrew package [Homebrew package](https://formulae.brew.sh/formula/dotdrop) Install dotdrop from Homebrew with: ```bash $ brew install dotdrop ``` ## Debian [Debian package](https://packages.debian.org/sid/dotdrop) Install dotdrop ```bash sudo apt install dotdrop ``` ## Ubuntu [Ubuntu package](https://packages.ubuntu.com/lunar/dotdrop) Install dotdrop ```bash $ sudo apt install dotdrop ``` ## Aur packages Dotdrop is available on aur: * Stable: * Git version: ## Snap package Dotdrop is available as a snap package: . Install it with: ```bash snap install dotdrop ``` If you encounter warnings like `Warning: using regular magic file`, try defining the following environment variable: ```bash export MAGIC=$SNAP/usr/share/file/magic.mgc ``` ## From source Clone the repository: ```bash $ git clone https://github.com/deadc0de6/dotdrop.git ``` Start using it directly through the `dotdrop.sh` script and use the `--cfg` switch to make it point to your config file. ```bash $ cd dotdrop/ $ ./dotdrop.sh --cfg files ``` ## Dependencies Beside the Python dependencies defined in [requirements.txt](https://github.com/deadc0de6/dotdrop/blob/master/requirements.txt), dotdrop depends on the following tools: * `diff` (unless a different tool is used, see [diff_command](config/config-config.md#config-block)) * `git` (only if using the entry point script [dotdrop.sh](https://github.com/deadc0de6/dotdrop/blob/master/dotdrop.sh)) * `readlink` or `realpath` (only if using the entry point script [dotdrop.sh](https://github.com/deadc0de6/dotdrop/blob/master/dotdrop.sh)) For macOS users, make sure to install the below packages through [Homebrew](https://brew.sh/): * [coreutils](https://formulae.brew.sh/formula/coreutils) (only if using the entry point script [dotdrop.sh](https://github.com/deadc0de6/dotdrop/blob/master/dotdrop.sh) which uses realpath) * [libmagic](https://formulae.brew.sh/formula/libmagic) (for python-magic) For WSL (Windows Subsystem for Linux), make sure to install `python-magic-bin`: ```bash pip install python-magic-bin ``` ## Shell completion Completion scripts exist for `bash`, `zsh` and `fish`; see [the related doc](https://github.com/deadc0de6/dotdrop/blob/master/completion/README.md). ## Highlighters Highlighters for dotdrop templates are available [here](https://github.com/deadc0de6/dotdrop/tree/master/highlighters). dotdrop-1.12.9/docs/misc/000077500000000000000000000000001436430751200151475ustar00rootroot00000000000000dotdrop-1.12.9/docs/misc/migrate-from-submodule.md000066400000000000000000000031361436430751200220620ustar00rootroot00000000000000**WARNING** Only do the following if you are using dotdrop version `< 0.7.1` or if you encounter an issue when running dotdrop that redirects you here. --- Initially dotdrop was only available as a submodule directly in the dotfiles git tree. When updated to work with PyPI, some code changed that brought some issues to older versions. If you want to keep it as a submodule (recommended), simply do the following: ```bash $ cd ## get latest version of the submodule $ git submodule foreach git pull origin master ## and stage the changes $ git add dotdrop $ git commit -m 'update dotdrop' ## update the bash script wrapper $ ./dotdrop/bootstrap.sh ## and stage the change to the dotdrop.sh script $ git add dotdrop.sh $ git commit -m 'update dotdrop.sh' ## and finally push the changes upstream $ git push ``` Otherwise, simply install it from PyPI as shown below: * Move to the dotfiles directory where dotdrop is used as a submodule ```bash $ cd ``` * Remove the entire `submodule "dotdrop"` section in `.gitmodules` * Stage the changes ```bash $ git add .gitmodules ``` * Remove the entire `submodule "dotdrop"` section in `.git/config` * Remove the submodule ```bash $ git rm --cached dotdrop ``` * Remove the submodule from .git ```bash $ rm -rf .git/modules/dotdrop ``` * Commit the changes ```bash $ git commit -m 'removing dotdrop submodule' ``` * Remove any remaining files from the dotdrop submodule ```bash $ rm -rf dotdrop ``` * Remove `dotdrop.sh` ```bash $ git rm dotdrop.sh $ git commit -m 'remove dotdrop.sh script' ``` * Push upstream ```bash $ git push ``` dotdrop-1.12.9/docs/misc/people-using-dotdrop.md000066400000000000000000000012501436430751200215470ustar00rootroot00000000000000For more examples, see how people are using dotdrop: * [https://github.com/open-dynaMIX/dotfiles](https://github.com/open-dynaMIX/dotfiles) * [https://github.com/japorized/dotfiles](https://github.com/japorized/dotfiles) * [https://github.com/whitelynx/dotfiles](https://github.com/whitelynx/dotfiles) * [https://github.com/Eluminae/dotfiles](https://github.com/Eluminae/dotfiles) * [https://github.com/davla/dotfiles](https://github.com/davla/dotfiles) Dotfiles repositories using dotdrop on [GitHub](https://github.com/topics/dotdrop?o=desc&s=updated). Or search directly on [GitHub](https://github.com/search?q=filename%3Aconfig.yaml+dotdrop&type=Code) for config examples. dotdrop-1.12.9/docs/template/000077500000000000000000000000001436430751200160275ustar00rootroot00000000000000dotdrop-1.12.9/docs/template/template-debug.md000066400000000000000000000003501436430751200212460ustar00rootroot00000000000000## Debugging templates To debug the result of a template, one can install the dotfiles to a temporary directory with the `install` command and the `-t` switch: ```bash $ dotdrop install -t Installed to tmp /tmp/dotdrop-6ajz7565 ```dotdrop-1.12.9/docs/template/template-filters.md000066400000000000000000000011541436430751200216330ustar00rootroot00000000000000# Template filters Besides [Jinja2 builtin filters](https://jinja.palletsprojects.com/en/2.11.x/templates/#builtin-filters), custom user-defined filter functions can be loaded using the config entry `filter_file`: Example: The config file: ```yaml config: filter_file: - /tmp/myfilter_file.py ``` The python filter under `/tmp/myfilter_file.py`: ```python def myfilter(arg1): return str(int(arg1) - 10) ``` The dotfile content: ``` {{@@ "13" | myfilter() @@}} ``` For more information on how to create filters, see [the Jinja2 official docs](https://jinja.palletsprojects.com/en/2.11.x/api/#writing-filters).dotdrop-1.12.9/docs/template/template-methods.md000066400000000000000000000023011436430751200216210ustar00rootroot00000000000000# Template methods Besides [Jinja2 global functions](https://jinja.palletsprojects.com/en/2.11.x/templates/#list-of-global-functions), the following methods can be used within templates: * `exists(path)`: returns true when path exists ``` {%@@ if exists('/dev/null') @@%} it does exist {%@@ endif @@%} ``` * `exists_in_path(name, path=None)`: returns true when executable exists in `$PATH` ``` {%@@ if exists_in_path('exa') @@%} alias ls='exa --git --color=always' {%@@ endif @@%} ``` * `basename(path)`: returns the `basename` of the path argument ``` {%@@ set dotfile_filename = basename( _dotfile_abs_dst ) @@%} dotfile dst filename: {{@@ dotfile_filename @@}} ``` * `dirname(path)`: returns the `dirname` of the path argument ``` {%@@ set dotfile_dirname = dirname( _dotfile_abs_dst ) @@%} dotfile dst dirname: {{@@ dotfile_dirname @@}} ``` Custom user-defined functions can be loaded with the help of the config entry `func_file`. Example: The config file: ```yaml config: func_file: - /tmp/myfuncs_file.py ``` The python function under `/tmp/myfuncs_file.py`: ```python def myfunc(arg): return not arg ``` The dotfile content: ``` {%@@ if myfunc(False) @@%} this should exist {%@@ endif @@%} ```dotdrop-1.12.9/docs/template/template-variables.md000066400000000000000000000062771436430751200221460ustar00rootroot00000000000000# Template variables ## Available variables The following variables are available in templates: * `{{@@ profile @@}}` contains the profile provided to dotdrop. * `{{@@ env['MY_VAR'] @@}}` contains environment variables (see [Environment variables](#environment-variables)). * `{{@@ header() @@}}` contains the dotdrop header (see [Dotdrop header](templating.md#dotdrop-header)). * `{{@@ _dotdrop_dotpath @@}}` contains the [dotpath](../config/config-config.md) absolute path. * `{{@@ _dotdrop_cfgpath @@}}` contains the [config file](../config/config-file.md) absolute path. * `{{@@ _dotdrop_workdir @@}}` contains the [workdir](../config/config-config.md) absolute path. * All variables defined [in the config](../config/config-file.md#variables) * Dotfile specific variables (see [Dotfile variables](#dotfile-variables)) ## Enriched variables The below variables are added to the available variables within templates. If the variable is already set by the user (through the config file for example) it will not be overwritten. * `{{@@ os @@}}` will contain the OS name as provided by * `{{@@ release @@}}` will contain the OS release version as provided by * `{{@@ distro_id @@}}` will contain the distribution ID as provided by * `{{@@ distro_version @@}}` will contain the distribution version as provided by * `{{@@ distro_like @@}}` will contain a space-separated list of distro IDs that are closely related to the current OS distro as provided by ## Dotfile variables When a dotfile is handled by dotdrop, the following variables are also available for templating: * `{{@@ _dotfile_abs_src @@}}` contains the processed dotfile absolute source path. * `{{@@ _dotfile_abs_dst @@}}` contains the processed dotfile absolute destination path. * `{{@@ _dotfile_key @@}}` contains the processed dotfile key. * `{{@@ _dotfile_link @@}}` contains the processed dotfile `link` string value. In addition to the above, the following variables are set in each file processed by dotdrop: * `{{@@ _dotfile_sub_abs_src @@}}` contains the absolute source path of each file when handled by dotdrop. * `{{@@ _dotfile_sub_abs_dst @@}}` contains the absolute destination path of each file when handled by dotdrop. For example, a directory dotfile (like `~/.ssh`) would process several files (`~/.ssh/config` and `~/.ssh/authorized_keys`, for example). In `~/.ssh/config`: * `_dotfile_abs_dst` would be `/home/user/.ssh` * `_dotfile_sub_abs_dst` would be `/home/user/.ssh/config` ## Environment variables It's possible to access environment variables inside the templates: ``` {{@@ env['MY_VAR'] @@}} ``` This allows for storing host-specific properties and/or secrets in environment variables. It is recommended to use `variables` (see [config variables](../config/config-file.md#variables)) instead of environment variables unless these contain sensitive information that shouldn't be versioned in Git (see [handle secrets doc](../howto/sensitive-dotfiles.md)).dotdrop-1.12.9/docs/template/templating.md000066400000000000000000000044661436430751200205270ustar00rootroot00000000000000# Templating Dotdrop leverages the power of [Jinja2](https://palletsprojects.com/p/jinja/) to handle the templating of dotfiles. See [the Jinja2 templates docs](https://jinja.palletsprojects.com/en/2.11.x/templates/) or the below sections for more information on how to template your dotfiles. ## Templating or not templating The dotfile config entry [template](../config/config-dotfiles.md#dotfiles-block) and the global config entry [template_dotfile_default](../config/config-config.md) allow to control whether a dotfile is processed by the templating engine. Obviously, if the dotfile uses template directives, it needs to be templated. However, if it is not, disabling templating will speed up its installation (since it won't have to be processed by the engine). For dotfiles being symlinked (`absolute`, `relative` or `link_children`), see [the dedicated doc](../howto/symlink-dotfiles.md#templating-symlinked-dotfiles). ## Delimiters Dotdrop uses different delimiters than [Jinja2](https://palletsprojects.com/p/jinja/)'s defaults: * Block/statement start = `{%@@` * Block/statement end = `@@%}` * Variable/expression start = `{{@@` * Variable/expression end = `@@}}` * Comment start = `{#@@` * Comment end = `@@#}` More info in [Jinja2 templating docs](https://jinja.palletsprojects.com/en/2.11.x/templates/?highlight=delimiter) ## Importing macros Macros must be imported `with context` in order to have access to the variables: ``` {%@@ from 'macro_file' import macro with context @@%} ``` For more information, see the [dedicated Jinja2 docs](https://jinja.palletsprojects.com/en/2.11.x/templates/#macros). ## Dotdrop header Dotdrop is able to insert a header in the generated dotfiles. This allows to remind anyone opening the file for editing that this file is managed by dotdrop. Here's what it looks like: ```none This dotfile is managed using dotdrop ``` The header can be automatically added with: ```none {{@@ header() @@}} ``` Properly commenting the header in templates is the responsibility of the user, as [Jinja2](https://palletsprojects.com/p/jinja/) has no way of knowing what is the proper char(s) used for comments. Either prepend the directive with the commenting char(s) used in the dotfile (for example `# {{@@ header() @@}}`) or provide it as an argument `{{@@ header('# ') @@}}`. The results are equivalent.dotdrop-1.12.9/docs/usage.md000066400000000000000000000247721436430751200156560ustar00rootroot00000000000000# Usage Run `dotdrop --help` to see all available options. ## Profile The default profile used by dotdrop is the *hostname* of the host you are running dotdrop on. It can be changed: * Using the command line switch `-p`/`--profile=` * By defining it in the env variable `DOTDROP_PROFILE` ## List profiles The `profiles` command lists the profiles defined in the config file. ```bash $ dotdrop profiles ``` Dotdrop allows to choose which profile to use with the `--profile` switch if you use something other than the default (the hostname). The default profile can also be changed by defining the `DOTDROP_PROFILE` environment variable. ## Import dotfiles The `import` command imports dotfiles to be managed by dotdrop. It copies the dotfile to your `dotpath` and updates the config file with the new entry. Dotdrop will ask whether to dereference symlinks on import unless `-f`/`--force` is used. For example, to import `~/.xinitrc`: ```bash $ dotdrop import ~/.xinitrc -> "/home/user/.xinitrc" imported 1 file(s) imported. ``` You can control how the dotfile key is generated in the config file with the following [config entries](config/config-config.md): * `longkey` * `false` (default): take the shortest unique path * `true` take the full path * `key_prefix`: defines if the key is prefixed with `f` for file and `d` for directory * `key_separator`: defines the separator to use (defaults to `_`) For example, `~/.config/awesome/rc.lua` gives: * `f_rc.lua` in the short format * `f_config_awesome_rc.lua` in the long format Importing `~/.mutt/colors` and then `~/.vim/colors` will result in: * `d_colors` and `d_vim_colors` in the short format * `d_mutt_colors` and `d_vim_colors` in the long format It is possible to import a dotfile while pretending it was at a different path with the use of `--as` what will effectively modify the `src` path of the generated dotfile entry in the config as well as the location of the file in the *dotpath*. The argument to `--as` is expected to be an absolute path and will be made absolute in case it isn't (specifying `--as test` will result in something like `--as /test`). It is however recommended to use [templating](template/templating.md) to avoid duplicates and optimize dotfile management instead of using `--as`. ```bash # imported to /zshrc.test $ dotdrop import ~/.zshrc --as=~/.zshrc.test ``` see [issue #220](https://github.com/deadc0de6/dotdrop/issues/220) and [issue #368](https://github.com/deadc0de6/dotdrop/issues/368). By importing a path using the profile special keyword `ALL`, a dotfile will be created in the config but won't be associated to any profile. To ignore specific patterns during import, see [the ignore patterns](config/config-file.md#ignore-patterns). For more options, see the usage with `dotdrop --help`. ## Install dotfiles The `install` command installs/deploys dotfiles managed by dotdrop from the `dotpath` to their destinations. ```bash $ dotdrop install ``` A dotfile will be installed only if it differs from the version already present at its destination. Some available options: * `-t`/`--temp`: Install the dotfile(s) to a temporary directory for review (helping to debug templating issues, for example). Note that actions are not executed in this mode. * `-a`/`--force-actions`: Force the execution of actions even if the dotfiles are not installed (see [Fake dotfile and actions](config/config-actions.md#fake-dotfile-and-actions) as an alternative) * `-f`/`--force`: Do not ask for any confirmation * `-W`/`--workdir-clear`: Clear the `workdir` before installing dotfiles (see [the config entry](config/config-config.md) `clear_workdir`) To ignore specific patterns during installation, see [the ignore patterns](config/config-file.md#ignore-patterns). For more options, see the usage with `dotdrop --help`. ## Compare dotfiles The `compare` command compares dotfiles at their destinations with the ones stored in your `dotpath`. ```bash $ dotdrop compare ``` The diffing is done with the UNIX tool `diff` as the backend; one can provide a specific diff command using [the config entry](config/config-config.md) `diff_command`. You can specify against which destination file to compare: ```bash $ dotdrop compare -C ~/.vimrc ``` To ignore specific patterns, see [the ignore patterns](config/config-file.md#ignore-patterns). To completely ignore all files not present in `dotpath` see [Ignore missing](config/config-file.md#ignore-missing). If you want to get notified on files present in the `workdir` but not tracked by dotdrop see the [compare_workdir](config/config-config.md). For more options, see the usage with `dotdrop --help`. ## List dotfiles The `files` command lists the dotfiles declared for a specific profile. ```bash $ dotdrop files --profile=some-profile f_xinitrc -> dst: /home/user/.xinitrc -> src: /home/user/dotdrop/dotfiles/xinitrc -> link: nolink ``` By using the `-T`/`--template` switch, only the dotfiles that are using [templating](template/templating.md) are listed. It is also possible to list all the files related to each dotfile entry by invoking the `detail` command, for example: ```bash $ dotdrop detail dotfiles details for profile "some-profile": f_xinitrc (dst: "/home/user/.xinitrc", link: nolink) -> /home/user/dotdrop/dotfiles/xinitrc (template:no) ``` This is especially useful when the dotfile entry is a directory and one wants to have information on the different files it contains (does a specific file uses templating, etc.). For more options, see the usage with `dotdrop --help`. ## Update dotfiles The `update` command updates a dotfile managed by dotdrop by copying the dotfile from the filesystem to the `dotpath`. Only dotfiles that have differences with the stored version are updated. A confirmation is requested from the user before any overwrite/update unless the `-f`/`--force` switch is used. Either provide the path of the file containing the new version of the dotfile or provide the dotfile key to update (as found in the config file) along with the `-k`/`--key` switch. When using the `-k`/`--key` switch and no key is provided, all dotfiles for that profile are updated. ```bash ## update by path $ dotdrop update ~/.vimrc ## update by key with the --key switch $ dotdrop update --key f_vimrc ``` If not argument is provided, all dotfiles for the selected profile are updated. To ignore specific patterns, see [the dedicated page](config/config-file.md#ignore-patterns). To completely ignore all files not present in `dotpath`, see [Ignore missing](config/config-file.md#ignore-missing). There are two cases when updating a dotfile: * [The dotfile does not use templating](#the-dotfile-does-not-use-templating) * [The dotfile uses templating](#the-dotfile-uses-templating) ### The dotfile does not use [templating](template/templating.md) The new version of the dotfile is copied to the *dotpath* directory and overwrites the old version. If Git is used to version the dotfiles stored by dotdrop, the Git command `diff` can be used to view the changes. ```bash $ dotdrop update ~/.vimrc $ git diff ``` ### The dotfile uses [templating](template/templating.md) The dotfile must be manually updated; three solutions can be used to identify the changes to apply to the template: * Use the `compare` command: ```bash ## use compare to identify change(s) $ dotdrop compare --file=~/.vimrc ``` * Call `update` with the `-P`/`--show-patch` switch, which provides an ad-hoc solution to manually patch the template file using a temporary generated version of the template. (This isn't a bullet-proof solution and might need manual checking.) ```bash ## get an ad-hoc solution to manually patch the template $ dotdrop update --show-patch ~/.vimrc [WARN] /home/user/dotfiles/vimrc uses template, update manually [WARN] try patching with: "diff -u /tmp/dotdrop-sbx6hw0r /home/user/.vimrc | patch /home/user/dotfiles/vimrc" ``` * Install the dotfiles to a temporary directory (using the `install` command and the `-t` switch) and compare the generated dotfile with the local one: ```bash ## use install to identify change(s) $ dotdrop install -t -t f_vimrc Installed to tmp /tmp/dotdrop-6ajz7565 $ diff ~/.vimrc /tmp/dotdrop-6ajz7565/home/user/.vimrc ``` ## Remove dotfiles The command `remove` allows to stop managing a specific dotfile with dotdrop. It will: * remove the entry from the config file (under `dotfiles` and `profile`) * delete the file from the `dotpath` For more options, see the usage with `dotdrop --help`. ## Concurrency The command line switch `-w`/`--workers`, if set to a value greater than one, enables the use of multiple concurrent workers to execute an operation. It can be applied to the following commands: * `install` * `compare` * `update` It should be set to a maximum of the number of cores available (usually returned on linux by the command `nproc`). It may speed up the operation but cannot be used interactively (it needs `-f`/`--force` to be set except for `compare`) and cannot be used with `-d`/`--dry`. Also, information printed to stdout/stderr will probably be messed up. **WARNING:** This feature hasn't been extensively tested and is to be used at your own risk. If you try it out and find any issues, please [report them](https://github.com/deadc0de6/dotdrop/issues). Also, if you find it useful and have been able to successfully speed up your operation when using `-w`/`--workers`, do please also report it [in an issue](https://github.com/deadc0de6/dotdrop/issues). ## Environment variables The following environment variables can be used to specify different CLI options. Note that CLI switches take precedence over environment variables (except for `DOTDROP_FORCE_NODEBUG`) * `DOTDROP_PROFILE`: `-p`/`--profile` ```bash export DOTDROP_PROFILE="my-fancy-profile" ``` * `DOTDROP_CONFIG`: `-c`/`--cfg` ```bash export DOTDROP_CONFIG="/home/user/dotdrop/config.yaml" ``` * `DOTDROP_NOBANNER`: `-b`/`--no-banner` ```bash export DOTDROP_NOBANNER= ``` * `DOTDROP_DEBUG`: `-V`/`--verbose` ```bash export DOTDROP_DEBUG= ``` * `DOTDROP_FORCE_NODEBUG`: disable debug output even if `-V`/`--verbose` is provided or `DOTDROP_DEBUG` is set ```bash export DOTDROP_FORCE_NODEBUG= ``` * `DOTDROP_TMPDIR`: defines a temporary directory for dotdrop to use for its operations instead of using a system generated one ```bash export DOTDROP_TMPDIR="/tmp/dotdrop-tmp" ``` * `DOTDROP_WORKDIR`: overwrite the `workdir` defined in the config ```bash export DOTDROP_WORKDIR="/tmp/dotdrop-workdir" ``` * `DOTDROP_WORKERS`: overwrite the `-w`/`--workers` cli argument ```bash export DOTDROP_WORKERS="10" ``` dotdrop-1.12.9/dotdrop.sh000077500000000000000000000021421436430751200152750ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # check for readlink/realpath presence # https://github.com/deadc0de6/dotdrop/issues/6 rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi # setup variables args=("$@") cur=$(dirname "$(${rl} "${0}")") opwd=$(pwd) cfg="${cur}/config.yaml" sub="dotdrop" # pivot cd "${cur}" || { echo "Directory \"${cur}\" doesn't exist, aborting." && exit 1; } # init/update the submodule if [ "${DOTDROP_AUTOUPDATE-yes}" = yes ] ; then git submodule update --init --recursive git submodule update --remote dotdrop fi # check python executable pybin="python3" hash ${pybin} 2>/dev/null || pybin="python" [[ "`${pybin} -V 2>&1`" =~ "Python 3" ]] || { echo "install Python 3" && exit 1; } # launch dotdrop PYTHONPATH=dotdrop:${PYTHONPATH} ${pybin} -m dotdrop.dotdrop "${args[@]}" ret="$?" # pivot back cd "${opwd}" || { echo "Directory \"${opwd}\" doesn't exist, aborting." && exit 1; } # exit with dotdrop exit code exit ${ret} dotdrop-1.12.9/dotdrop.svg000066400000000000000000000062011436430751200154570ustar00rootroot00000000000000 dotdrop-1.12.9/dotdrop/000077500000000000000000000000001436430751200147375ustar00rootroot00000000000000dotdrop-1.12.9/dotdrop/__init__.py000066400000000000000000000003751436430751200170550ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 """ # pylint: disable=C0415 import sys def main(): """entry point""" import dotdrop.dotdrop if dotdrop.dotdrop.main(): sys.exit(0) sys.exit(1) dotdrop-1.12.9/dotdrop/action.py000066400000000000000000000121001436430751200165600ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 represent an action or transformation in dotdrop """ import subprocess import os # local imports from dotdrop.dictparser import DictParser from dotdrop.exceptions import UndefinedException class Cmd(DictParser): """A command to execute""" args = [] eq_ignore = ('log',) descr = 'command' def __init__(self, key, action): """constructor @key: action key @action: action string @silent: action silent """ self.key = key self.action = action self.silent = key.startswith('_') def _get_action(self, templater, debug): action = None try: action = templater.generate_string(self.action) except UndefinedException as exc: err = f'undefined variable for {self.descr}: \"{exc}\"' self.log.warn(err) return False if debug: self.log.dbg(f'{self.descr}:') self.log.dbg(f' - raw \"{self.action}\"') self.log.dbg(f' - templated \"{action}\"') return action def _get_args(self, templater): args = [] if not self.args: return args args = self.args if templater: try: args = [templater.generate_string(a) for a in args] except UndefinedException as exc: err = f'undefined arguments for {self.descr}: {exc}' self.log.warn(err) return False return args def execute(self, templater=None, debug=False): """execute the command in the shell""" ret = 1 action = self.action if templater: action = self._get_action(templater, debug) args = self._get_args(templater) if debug and args: self.log.dbg('action args:') for cnt, arg in enumerate(args): self.log.dbg(f'\targs[{cnt}]: {arg}') try: cmd = action.format(*args) except IndexError as exc: err = f'index error for {self.descr}: \"{action}\"' err += f' with \"{args}\"' err += f': {exc}' self.log.warn(err) return False except KeyError as exc: err = f'key error for {self.descr}: \"{action}\": {exc}' err += f' with \"{args}\"' self.log.warn(err) return False if self.silent: self.log.sub(f'executing silent action \"{self.key}\"') if debug: self.log.dbg('action cmd silenced') else: if debug: self.log.dbg(f'action cmd: \"{cmd}\"') self.log.sub(f'executing \"{cmd}\"') try: ret = subprocess.call(cmd, shell=True) except KeyboardInterrupt: self.log.warn(f'{self.descr} interrupted') if ret != 0: self.log.warn(f'{self.descr} returned code {ret}') return ret == 0 @classmethod def _adjust_yaml_keys(cls, value): return {'action': value} def __str__(self): return f'key:{self.key} -> \"{self.action}\"' class Action(Cmd): """An action to execute""" pre = 'pre' post = 'post' descr = 'action' def __init__(self, key, kind, action): """constructor @key: action key @kind: type of action (pre or post) @action: action string """ super().__init__(key, action) self.kind = kind self.args = [] def copy(self, args): """return a copy of this object with arguments""" action = Action(self.key, self.kind, self.action) action.args = args return action @classmethod def parse(cls, key, value): """parse key value into object""" val = {} val['kind'], val['action'] = value return cls(key=key, **val) def __str__(self): out = f'{self.key}: [{self.kind}] \"{self.action}\"' return out def __repr__(self): return f'action({self.__str__()})' class Transform(Cmd): """A transformation on a dotfile""" descr = 'transformation' def __init__(self, key, action): """constructor @key: action key @trans: action string """ super().__init__(key, action) self.args = [] def copy(self, args): """return a copy of this object with arguments""" trans = Transform(self.key, self.action) trans.args = args return trans def transform(self, arg0, arg1, templater=None, debug=False): """ execute transformation with {0} and {1} where {0} is the file to transform and {1} is the result file """ if os.path.exists(arg1): msg = f'transformation \"{self.key}\": destination exists: {arg1}' self.log.warn(msg) return False if not self.args: self.args = [] self.args.insert(0, arg1) self.args.insert(0, arg0) return self.execute(templater=templater, debug=debug) dotdrop-1.12.9/dotdrop/cfg_aggregator.py000066400000000000000000000447701436430751200202660ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 handle higher level of the config file """ import os import shlex import platform import distro # local imports from dotdrop.cfg_yaml import CfgYaml from dotdrop.dotfile import Dotfile from dotdrop.settings import Settings from dotdrop.profile import Profile from dotdrop.action import Action, Transform from dotdrop.logger import Logger from dotdrop.utils import strip_home, debug_list, debug_dict from dotdrop.exceptions import UndefinedException, YamlException TILD = '~' class CfgAggregator: """The config aggregator class""" file_prefix = 'f' dir_prefix = 'd' variable_os = 'os' variable_release = 'release' variable_distro_id = 'distro_id' variable_distro_like = 'distro_like' variable_distro_version = 'distro_version' def __init__(self, path, profile_key, debug=False, dry=False): """ high level config parser @path: path to the config file @profile_key: profile key @debug: debug flag """ self.path = path self.profile_key = profile_key self.debug = debug self.dry = dry self.log = Logger(debug=self.debug) try: self._load() except Exception as exc: raise YamlException(exc) from exc self._validate() ######################################################## # public methods ######################################################## def del_dotfile(self, dotfile): """remove this dotfile from the config""" return self.cfgyaml.del_dotfile(dotfile.key) def del_dotfile_from_profile(self, dotfile, profile): """remove this dotfile from this profile""" return self.cfgyaml.del_dotfile_from_profile(dotfile.key, profile.key) def new_dotfile(self, src, dst, link, chmod=None, trans_read=None, trans_write=None): """ import a new dotfile @src: path in dotpath @dst: path in FS @link: LinkType @chmod: file permission @trans_read: read transformation @trans_write: write transformation """ dst = self.path_to_dotfile_dst(dst) dotfile = self.get_dotfile_by_src_dst(src, dst) if not dotfile: # add the dotfile dotfile = self._create_new_dotfile(src, dst, link, chmod=chmod, trans_read=trans_read, trans_write=trans_write) if not dotfile: return False ret = dotfile is not None if self.profile_key != self.cfgyaml.key_all: # add to profile key = dotfile.key ret = self.cfgyaml.add_dotfile_to_profile(key, self.profile_key) if ret: msg = f'new dotfile {key} to profile {self.profile_key}' self.log.dbg(msg) # save the config and reload it if ret: self._save_and_reload() return ret def update_dotfile(self, key, chmod): """update an existing dotfile""" ret = self.cfgyaml.update_dotfile(key, chmod) if ret: self._save_and_reload() return ret def path_to_dotfile_dst(self, path): """normalize the path to match dotfile dst""" path = self._norm_path(path) # use tild for home home = os.path.expanduser(TILD) + os.sep if path.startswith(home): path = path[len(home):] path = os.path.join(TILD, path) return path def get_dotfile_by_dst(self, dst, profile_key=None): """ get a list of dotfiles by dst @dst: dotfile dst (on filesystem) """ dotfiles = [] dst = self._norm_path(dst) dfs = self.dotfiles if profile_key: dfs = self.get_dotfiles(profile_key=profile_key) for dotfile in dfs: left = self._norm_path(dotfile.dst) if left == dst: dotfiles.append(dotfile) return dotfiles def get_dotfile_by_src_dst(self, src, dst): """ get a dotfile by src and dst @src: dotfile src (in dotpath) @dst: dotfile dst (on filesystem) """ if not os.path.isabs(src): # ensures we have an absolute path try: src = self.cfgyaml.resolve_dotfile_src(src) except UndefinedException as exc: err = f'unable to resolve {src}: {exc}' self.log.err(err) return None dotfiles = self.get_dotfile_by_dst(dst) for dotfile in dotfiles: dsrc = self.cfgyaml.resolve_dotfile_src(dotfile.src) if dsrc == src: return dotfile return None def save(self): """save the config""" if self.dry: return True return self.cfgyaml.save() def dump(self): """dump the config dictionary""" return self.cfgyaml.dump() def get_settings(self): """return settings as a dict""" return self.settings.serialize()[Settings.key_yaml] def get_variables(self): """return variables""" return self.variables def get_profiles(self): """return profiles""" return self.profiles def get_profile(self, key=None): """return profile object""" pro = self.profile_key if key: pro = key try: return next(x for x in self.profiles if x.key == pro) except StopIteration: return None def get_profiles_by_dotfile_key(self, key): """return all profiles having this dotfile""" res = [] for profile in self.profiles: keys = [dotfile.key for dotfile in profile.dotfiles] if key in keys: res.append(profile) return res def get_dotfiles(self, profile_key=None): """ get all dotfiles for the current profile if None or the specified profile_key if defined or all dotfiles if profile_key is ALL """ if profile_key == self.cfgyaml.key_all: return self.dotfiles dotfiles = [] profile = self.get_profile(key=profile_key) if not profile: return dotfiles return profile.dotfiles def get_dotfile(self, key, profile_key=None): """ return dotfile object by key @key: the dotfile key to look for """ dfs = self.dotfiles if profile_key: profile = self.get_profile(key=profile_key) if not profile: return None dfs = profile.dotfiles try: return next(x for x in dfs if x.key == key) except StopIteration: return None ######################################################## # accessors for public methods ######################################################## def _create_new_dotfile(self, src, dst, link, chmod=None, trans_read=None, trans_write=None): """create a new dotfile""" # get a new dotfile with a unique key key = self._get_new_dotfile_key(dst) self.log.dbg(f'new dotfile key: {key}') # add the dotfile trans_r_key = trans_w_key = None if trans_read: trans_r_key = trans_read.key if trans_write: trans_w_key = trans_write.key if not self.cfgyaml.add_dotfile(key, src, dst, link, chmod=chmod, trans_r_key=trans_r_key, trans_w_key=trans_w_key): return None return Dotfile(key, dst, src, trans_r=trans_read, trans_w=trans_write) ######################################################## # parsing ######################################################## def _validate(self): """validate fields on top level view of config""" val = self.settings.workdir if not val: raise UndefinedException('\"workdir\" is undefined') def _load(self, reloading=False): """load lower level config""" self.cfgyaml = CfgYaml(self.path, self.profile_key, reloading=reloading, debug=self.debug) # settings self.settings = Settings.parse(None, self.cfgyaml.settings) self.key_prefix = self.settings.key_prefix self.key_separator = self.settings.key_separator # dotfiles self.dotfiles = Dotfile.parse_dict(self.cfgyaml.dotfiles) debug_list('dotfiles', self.dotfiles, self.debug) # profiles self.profiles = Profile.parse_dict(self.cfgyaml.profiles) debug_list('profiles', self.profiles, self.debug) # actions self.actions = Action.parse_dict(self.cfgyaml.actions) debug_list('actions', self.actions, self.debug) # trans_r self.trans_r = Transform.parse_dict(self.cfgyaml.trans_r) debug_list('trans_r', self.trans_r, self.debug) # trans_w self.trans_w = Transform.parse_dict(self.cfgyaml.trans_w) debug_list('trans_w', self.trans_w, self.debug) # variables self.variables = self.cfgyaml.variables debug_dict('variables', self.variables, self.debug) self._enrich_variables() # patch dotfiles in profiles self._patch_keys_to_objs(self.profiles, "dotfiles", self.get_dotfile) # patch action in dotfiles actions self._patch_keys_to_objs(self.dotfiles, "actions", self._get_action_w_args) # patch action in profiles actions self._patch_keys_to_objs(self.profiles, "actions", self._get_action_w_args) # patch actions in settings default_actions self._patch_keys_to_objs([self.settings], "default_actions", self._get_action_w_args) msg = f'default actions: {self.settings.default_actions}' self.log.dbg(msg) # patch trans_w/trans_r in dotfiles self._patch_keys_to_objs(self.dotfiles, "trans_r", self._get_trans_w_args(self.get_trans_r), islist=False) self._patch_keys_to_objs(self.dotfiles, "trans_w", self._get_trans_w_args(self.get_trans_w), islist=False) def _enrich_variables(self): """ enrich available variables """ if self.variable_os not in self.variables: # enrich with os variable # https://docs.python.org/3/library/platform.html#platform.system var_os = platform.system().lower() self.variables[self.variable_os] = var_os msg = f'enrich variables with {self.variable_os}={var_os}' self.log.dbg(msg) if self.variable_release not in self.variables: # enrich with release variable # https://docs.python.org/3/library/platform.html#platform.release var_release = platform.release().lower() self.variables[self.variable_release] = var_release msg = f'enrich variables with {self.variable_release}' msg += f'={var_release}' self.log.dbg(msg) if self.variable_distro_id not in self.variables: # enrich with distro variable # https://pypi.org/project/distro/ # https://distro.readthedocs.io/en/latest/#distro.id var_distro_id = distro.id().lower() self.variables[self.variable_distro_id] = var_distro_id msg = f'enrich variables with {self.variable_distro_id}' msg += f'={var_distro_id}' self.log.dbg(msg) if self.variable_distro_version not in self.variables: # enrich with distro variable # https://pypi.org/project/distro/ # https://distro.readthedocs.io/en/latest/#distro.version var_version = distro.version().lower() self.variables[self.variable_distro_version] = var_version msg = f'enrich variables with {self.variable_distro_version}' msg += f'={var_version}' self.log.dbg(msg) if self.variable_distro_like not in self.variables: # enrich with distro variable # https://pypi.org/project/distro/ # https://distro.readthedocs.io/en/latest/#distro.like var_like = distro.like().lower() self.variables[self.variable_distro_like] = var_like msg = f'enrich variables with {self.variable_distro_like}' msg += f'={var_like}' self.log.dbg(msg) def _patch_keys_to_objs(self, containers, keys, get_by_key, islist=True): """ map for each key in the attribute 'keys' in 'containers' the returned object from the method 'get_by_key' """ if not containers: return self.log.dbg(f'patching {keys} ...') for container in containers: objects = [] okeys = getattr(container, keys) if not okeys: continue if not islist: okeys = [okeys] for key in okeys: obj = get_by_key(key) if not obj: err = f'{container} does not contain' err += f' a {keys} entry named {key}' self.log.err(err) raise Exception(err) objects.append(obj) if not islist: objects = objects[0] setattr(container, keys, objects) ######################################################## # dotfile key ######################################################## def _get_new_dotfile_key(self, dst): """return a new unique dotfile key""" path = os.path.expanduser(dst) existing_keys = self.cfgyaml.get_all_dotfile_keys() if self.settings.longkey: return self._get_long_key(path, existing_keys) return self._get_short_key(path, existing_keys) @classmethod def _norm_key_elem(cls, elem): """normalize path element for sanity""" elem = elem.lstrip('.') elem = elem.replace(' ', '-') return elem.lower() def _get_long_key(self, path, keys): """ return a unique long key representing the absolute path of path """ dirs = self._split_path_for_key(path) prefix = [] if self.key_prefix: prefix = [self.file_prefix] if os.path.isdir(path): prefix = [self.dir_prefix] key = self.key_separator.join(prefix + dirs) return self._uniq_key(key, keys) def _get_short_key(self, path, keys): """ return a unique key where path is known not to be an already existing dotfile """ dirs = self._split_path_for_key(path) dirs.reverse() prefix = [] if self.key_prefix: prefix = [self.file_prefix] if os.path.isdir(path): prefix = [self.dir_prefix] entries = [] for dri in dirs: entries.insert(0, dri) key = self.key_separator.join(prefix + entries) if key not in keys: return key return self._uniq_key(key, keys) def _uniq_key(self, key, keys): """unique dotfile key""" newkey = key cnt = 1 while newkey in keys: # if unable to get a unique path # get a random one newkey = self.key_separator.join([key, str(cnt)]) cnt += 1 return newkey ######################################################## # helpers ######################################################## def _save_and_reload(self): if self.dry: return self.save() self.log.dbg('reloading config') olddebug = self.debug self.debug = False self._load(reloading=True) self.debug = olddebug @classmethod def _norm_path(cls, path): if not path: return path path = os.path.expanduser(path) path = os.path.expandvars(path) path = os.path.abspath(path) return path def _split_path_for_key(self, path): """return a list of path elements, excluded home path""" path = strip_home(path) dirs = [] while True: path, file = os.path.split(path) dirs.append(file) if not path or not file: break dirs.reverse() # remove empty entries dirs = filter(None, dirs) # normalize entries return list(map(self._norm_key_elem, dirs)) def _get_action(self, key): """return action by key""" try: return next(x for x in self.actions if x.key == key) except StopIteration: return None def _get_action_w_args(self, key): """return action by key with the arguments""" fields = shlex.split(key) if len(fields) > 1: # we have args key, *args = fields msg = f'action with parm: {key} and {args}' self.log.dbg(msg) action = self._get_action(key).copy(args) else: action = self._get_action(key) return action def _get_trans_w_args(self, getter): """return transformation by key with the arguments""" def getit(key): fields = shlex.split(key) if len(fields) > 1: # we have args key, *args = fields msg = f'trans with parm: {key} and {args}' self.log.dbg(msg) trans = getter(key).copy(args) else: trans = getter(key) return trans return getit def get_trans_r(self, key): """return the trans_r with this key""" try: return next(x for x in self.trans_r if x.key == key) except StopIteration: return None def get_trans_w(self, key): """return the trans_w with this key""" try: return next(x for x in self.trans_w if x.key == key) except StopIteration: return None dotdrop-1.12.9/dotdrop/cfg_yaml.py000066400000000000000000002027601436430751200171010ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 handle lower level of the config file will provide the following dictionaries to the upper layer: * self.settings * self.dotfiles * self.profiles * self.actions * self.trans_r * self.trans_w * self.variables Additionally a few methods are exported. """ # pylint: disable=C0302 import os import glob import io from copy import deepcopy from itertools import chain from ruamel.yaml import YAML as yaml import toml # local imports from dotdrop.version import __version__ as VERSION from dotdrop.settings import Settings from dotdrop.logger import Logger from dotdrop.templategen import Templategen from dotdrop.linktypes import LinkTypes from dotdrop.utils import shellrun, uniq_list, userinput from dotdrop.exceptions import YamlException, UndefinedException class CfgYaml: """yaml config file parser""" # global entries key_settings = Settings.key_yaml key_dotfiles = 'dotfiles' key_profiles = 'profiles' key_actions = 'actions' old_key_trans_r = 'trans' key_trans_r = 'trans_read' key_trans_w = 'trans_write' key_variables = 'variables' key_dvariables = 'dynvariables' key_uvariables = 'uservariables' action_pre = 'pre' action_post = 'post' save_uservariables_name = 'uservariables{}.yaml' # profiles/dotfiles entries key_dotfile_src = 'src' key_dotfile_dst = 'dst' key_dotfile_link = 'link' key_dotfile_actions = 'actions' key_dotfile_noempty = 'ignoreempty' key_dotfile_template = 'template' key_dotfile_chmod = 'chmod' # chmod value chmod_ignore = 'preserve' # profile key_profile_dotfiles = 'dotfiles' key_profile_include = 'include' key_profile_variables = 'variables' key_profile_dvariables = 'dynvariables' key_profile_actions = 'actions' key_all = 'ALL' # import entries key_import_actions = 'import_actions' key_import_configs = 'import_configs' key_import_variables = 'import_variables' key_import_profile_dfs = 'import' key_import_sep = ':' key_import_ignore_key = 'optional' key_import_fatal_not_found = True # settings key_settings_dotpath = Settings.key_dotpath key_settings_workdir = Settings.key_workdir key_settings_link_dotfile_default = Settings.key_link_dotfile_default key_settings_noempty = Settings.key_ignoreempty key_settings_minversion = Settings.key_minversion key_imp_link = Settings.key_link_on_import key_settings_template = Settings.key_template_dotfile_default # link values lnk_nolink = LinkTypes.NOLINK.name.lower() lnk_link = LinkTypes.LINK.name.lower() lnk_children = LinkTypes.LINK_CHILDREN.name.lower() lnk_absolute = LinkTypes.ABSOLUTE.name.lower() lnk_relative = LinkTypes.RELATIVE.name.lower() # checks allowed_link_val = [lnk_nolink, lnk_link, lnk_children, lnk_absolute, lnk_relative] top_entries = [key_dotfiles, key_settings, key_profiles] def __init__(self, path, profile=None, addprofiles=None, reloading=False, debug=False, imported_configs=None): """ config parser @path: config file path @profile: the selected profile names @addprofiles: included profiles names (list) @reloading: true when reloading @imported_configs: paths of config files that have been imported so far @debug: debug flag """ self._path = os.path.abspath(path) self._profile = profile self._reloading = reloading self._debug = debug self._log = Logger(debug=self._debug) # config format self._config_format = 'yaml' # config needs to be written self._dirty = False # indicates the config has been updated self._dirty_deprecated = False # profile variables self._profilevarskeys = [] # included profiles self._inc_profiles = addprofiles or [] # imported configs self.imported_configs = imported_configs or [] # init the dictionaries self.settings = {} self.dotfiles = {} self.profiles = {} self.actions = {} self.trans_r = {} self.trans_w = {} self.variables = {} if not os.path.exists(self._path): err = f'invalid config path: \"{path}\"' if self._debug: self._dbg(err) raise YamlException(err) self._dbg('START of config parsing') self._dbg(f'reloading: {reloading}') self._dbg(f'profile: {profile}') pfs = ','.join(self._inc_profiles) self._dbg(f'included profiles: {pfs}') self._yaml_dict = self._load_yaml(self._path) # live patch deprecated entries self._fix_deprecated(self._yaml_dict) # validate content self._validate(self._yaml_dict) ################################################## # parse the config and variables ################################################## # parse the "config" block self.settings = self._parse_blk_settings(self._yaml_dict) # base templater (when no vars/dvars exist) self.variables = self._enrich_vars(self.variables, self._profile) self._redefine_templater() # variables and dynvariables need to be first merged # before being templated in order to allow cyclic # references between them # parse the "variables" block var = self._parse_blk_variables(self._yaml_dict) self._add_variables(var, template=False) # parse the "dynvariables" block dvariables = self._parse_blk_dynvariables(self._yaml_dict) self._add_variables(dvariables, template=False) # now template variables and dynvariables from the same pool self._rec_resolve_variables(self.variables) # and execute dvariables # since this is done after recursively resolving variables # and dynvariables this means that variables referencing # dynvariables will result with the not executed value if dvariables.keys(): self._shell_exec_dvars(self.variables, keys=dvariables.keys()) # finally redefine the template self._redefine_templater() if self._debug: title = 'current variables defined' self._debug_dict(title, self.variables) # parse the "profiles" block self.profiles = self._parse_blk_profiles(self._yaml_dict) # include the profile's variables/dynvariables last # as it overwrites existing ones incpro, pvar, pdvar = self._get_profile_included_vars() self._inc_profiles = uniq_list(self._inc_profiles + incpro) self._add_variables(pvar, prio=True) self._add_variables(pdvar, shell=True, prio=True) self._profilevarskeys.extend(pvar.keys()) self._profilevarskeys.extend(pdvar.keys()) # template variables self.variables = self._template_dict(self.variables) if self._debug: title = 'variables defined (after template dict)' self._debug_dict(title, self.variables) ################################################## # template the "include" entries ################################################## self._template_include_entry() if self._debug: title = 'variables defined (after template include)' self._debug_dict(title, self.variables) ################################################## # template config entries ################################################## entry = self.settings[self.key_settings_dotpath] val = self._template_item(entry) self.settings[self.key_settings_dotpath] = val ################################################## # parse the other blocks ################################################## # parse the "dotfiles" block self.dotfiles = self._parse_blk_dotfiles(self._yaml_dict) # parse the "actions" block self.actions = self._parse_blk_actions(self._yaml_dict) # parse the "trans_r" block self.trans_r = self._parse_blk_trans_r(self._yaml_dict) # parse the "trans_w" block self.trans_w = self._parse_blk_trans_w(self._yaml_dict) ################################################## # import elements ################################################## # process imported variables (import_variables) newvars = self._import_variables() self._clear_profile_vars(newvars) self._add_variables(newvars) # process imported actions (import_actions) self._import_actions() # process imported profile dotfiles (import) self._import_profiles_dotfiles() # process imported configs (import_configs) self._import_configs() # process profile include items (actions, dotfiles, ...) self._resolve_profile_includes() # add the current profile variables _, pvar, pdvar = self._get_profile_included_vars() self._add_variables(pvar, prio=False) self._add_variables(pdvar, shell=True, prio=False) self._profilevarskeys.extend(pvar.keys()) self._profilevarskeys.extend(pdvar.keys()) # resolve variables self._clear_profile_vars(newvars) self._add_variables(newvars) # process profile ALL self._resolve_profile_all() # template dotfiles entries self._template_dotfiles_entries() # parse the "uservariables" block uvariables = self._parse_blk_uservariables(self._yaml_dict, self.variables) self._add_variables(uvariables, template=False, prio=False) # end of parsing if self._debug: self._dbg('########### final config ###########') self._debug_entries() self._dbg('END of config parsing') ######################################################## # public methods ######################################################## def _resolve_dotfile_link(self, link): """resolve dotfile link entry""" newlink = self._template_item(link) # check link value if newlink not in self.allowed_link_val: err = f'bad link value: {newlink}' self._log.err(err) self._log.err(f'allowed: {self.allowed_link_val}') raise YamlException(f'config content error: {err}') return newlink def resolve_dotfile_src(self, src, templater=None): """ get abs src file from a relative path in dotpath """ newsrc = '' if src: new = src if templater: new = templater.generate_string(src) if new != src and self._debug: msg = f'dotfile src: \"{src}\" -> \"{new}\"' self._dbg(msg) src = new src = os.path.join(self.settings[self.key_settings_dotpath], src) newsrc = self._norm_path(src) return newsrc def resolve_dotfile_dst(self, dst, templater=None): """resolve dotfile dst path""" newdst = '' if dst: new = dst if templater: new = templater.generate_string(dst) if new != dst and self._debug: msg = f'dotfile dst: \"{dst}\" -> \"{new}\"' self._dbg(msg) dst = new newdst = self._norm_path(dst) return newdst def add_dotfile_to_profile(self, dotfile_key, profile_key): """ add an existing dotfile key to a profile_key if profile_key doesn't exist, the profile is created we test using profiles variable since it merges imported ones (include, etc) but insert in main yaml only return True if was added """ # create the profile if it doesn't exist self._new_profile(profile_key) if profile_key not in self.profiles: return False profile = self.profiles[profile_key] # ensure profile dotfiles list is not None in local object if self.key_profile_dotfiles not in profile or \ profile[self.key_profile_dotfiles] is None: profile[self.key_profile_dotfiles] = [] # ensure profile dotfiles list is not None in yaml dict dict_pro = self._yaml_dict[self.key_profiles][profile_key] if self.key_profile_dotfiles not in dict_pro or \ dict_pro[self.key_profile_dotfiles] is None: dict_pro[self.key_profile_dotfiles] = [] # add to the profile pdfs = profile[self.key_profile_dotfiles] if self.key_all not in pdfs and \ dotfile_key not in pdfs: # append dotfile pro = self._yaml_dict[self.key_profiles][profile_key] pro[self.key_profile_dotfiles].append(dotfile_key) if self._debug: msg = f'add \"{dotfile_key}\" to profile \"{profile_key}\"' self._dbg(msg) self._dirty = True return self._dirty def get_all_dotfile_keys(self): """return all existing dotfile keys""" return self.dotfiles.keys() def _update_dotfile_chmod(self, key, dotfile, chmod): old = None if self.key_dotfile_chmod in dotfile: old = dotfile[self.key_dotfile_chmod] if old == self.chmod_ignore: msg = ( 'ignore chmod change since ' f'{self.chmod_ignore}' ) self._dbg(msg) return False if old == chmod: return False if self._debug: self._dbg(f'update dotfile: {key}') self._dbg(f'old chmod value: {old}') self._dbg(f'new chmod value: {chmod}') dotfile = self._yaml_dict[self.key_dotfiles][key] if not chmod: del dotfile[self.key_dotfile_chmod] else: dotfile[self.key_dotfile_chmod] = str(format(chmod, 'o')) return True def update_dotfile(self, key, chmod): """ update an existing dotfile return true if updated """ if key not in self.dotfiles.keys(): return False dotfile = self._yaml_dict[self.key_dotfiles][key] if not self._update_dotfile_chmod(key, dotfile, chmod): return False self._dirty = True return True def add_dotfile(self, key, src, dst, link, chmod=None, trans_r_key=None, trans_w_key=None): """add a new dotfile""" if key in self.dotfiles.keys(): return False if self._debug: self._dbg(f'adding new dotfile: {key}') self._dbg(f'new dotfile src: {src}') self._dbg(f'new dotfile dst: {dst}') self._dbg(f'new dotfile link: {link}') self._dbg(f'new dotfile chmod: {chmod}') self._dbg(f'new dotfile trans_r: {trans_r_key}') self._dbg(f'new dotfile trans_w: {trans_w_key}') # create the dotfile dict df_dict = { self.key_dotfile_src: src, self.key_dotfile_dst: dst, } # link dfl = self.settings[self.key_settings_link_dotfile_default] if str(link) != dfl: df_dict[self.key_dotfile_link] = str(link) # chmod if chmod: df_dict[self.key_dotfile_chmod] = str(format(chmod, 'o')) # trans_r/trans_w if trans_r_key: df_dict[self.key_trans_r] = str(trans_r_key) if trans_w_key: df_dict[self.key_trans_w] = str(trans_w_key) if self._debug: self._dbg(f'dotfile dict: {df_dict}') # add to global dict self._yaml_dict[self.key_dotfiles][key] = df_dict self._dirty = True return True def del_dotfile(self, key): """remove this dotfile from config""" if key not in self._yaml_dict[self.key_dotfiles]: self._log.err(f'key not in dotfiles: {key}') return False if self._debug: self._dbg(f'remove dotfile: {key}') del self._yaml_dict[self.key_dotfiles][key] if self._debug: dfs = self._yaml_dict[self.key_dotfiles] self._dbg(f'new dotfiles: {dfs}') self._dirty = True return True def del_dotfile_from_profile(self, df_key, pro_key): """remove this dotfile from that profile""" if self._debug: self._dbg(f'removing \"{df_key}\" from \"{pro_key}\"') if df_key not in self.dotfiles.keys(): self._log.err(f'key not in dotfiles: {df_key}') return False if pro_key not in self.profiles.keys(): self._log.err(f'key not in profile: {pro_key}') return False # get the profile dictionary profile = self._yaml_dict[self.key_profiles][pro_key] if self.key_profile_dotfiles not in profile: # profile does not contain any dotfiles return True if df_key not in profile[self.key_profile_dotfiles]: return True if self._debug: dfs = profile[self.key_profile_dotfiles] self._dbg(f'{pro_key} profile dotfiles: {dfs}') self._dbg(f'remove {df_key} from profile {pro_key}') profile[self.key_profile_dotfiles].remove(df_key) if self._debug: dfs = profile[self.key_profile_dotfiles] self._dbg(f'{pro_key} profile dotfiles: {dfs}') self._dirty = True return True def save(self): """save this instance and return True if saved""" if not self._dirty: return False content = self._prepare_to_save(self._yaml_dict) if self._dirty_deprecated: # add minversion settings = content[self.key_settings] settings[self.key_settings_minversion] = VERSION # save to file if self._debug: self._dbg(f'saving to {self._path}') try: with open(self._path, 'w', encoding='utf8') as file: self._yaml_dump(content, file, fmt=self._config_format) except Exception as exc: self._log.err(exc) err = f'error saving config: {self._path}' raise YamlException(err) from exc if self._dirty_deprecated: warn = 'your config contained deprecated entries' warn += ' and was updated' self._log.warn(warn) self._dirty = False return True def dump(self): """dump the config dictionary""" output = io.StringIO() content = self._prepare_to_save(self._yaml_dict.copy()) self._yaml_dump(content, output, fmt=self._config_format) return output.getvalue() ######################################################## # block parsing ######################################################## def _parse_blk_settings(self, dic): """parse the "config" block""" block = self._get_entry(dic, self.key_settings).copy() # set defaults settings = Settings(None).serialize().get(self.key_settings) settings.update(block) # resolve minimum version if self.key_settings_minversion in settings: minversion = settings[self.key_settings_minversion] self._check_minversion(minversion) # normalize paths paths = self._norm_path(settings[self.key_settings_dotpath]) settings[self.key_settings_dotpath] = paths paths = self._norm_path(settings[self.key_settings_workdir]) settings[self.key_settings_workdir] = paths paths = [ self._norm_path(path) for path in settings[Settings.key_filter_file] ] settings[Settings.key_filter_file] = paths paths = [ self._norm_path(path) for path in settings[Settings.key_func_file] ] settings[Settings.key_func_file] = paths if self._debug: self._debug_dict('settings block:', settings) return settings def _parse_blk_dotfiles(self, dic): """parse the "dotfiles" block""" dotfiles = self._get_entry(dic, self.key_dotfiles).copy() keys = dotfiles.keys() if len(keys) != len(list(set(keys))): dups = [x for x in keys if x not in list(set(keys))] err = f'duplicate dotfile keys found: {dups}' raise YamlException(err) dotfiles = self._norm_dotfiles(dotfiles) if self._debug: self._debug_dict('dotfiles block', dotfiles) return dotfiles def _parse_blk_profiles(self, dic): """parse the "profiles" block""" profiles = self._get_entry(dic, self.key_profiles).copy() profiles = self._norm_profiles(profiles) if self._debug: self._debug_dict('profiles block', profiles) return profiles def _parse_blk_actions(self, dic): """parse the "actions" block""" actions = self._get_entry(dic, self.key_actions, mandatory=False) if actions: actions = actions.copy() actions = self._norm_actions(actions) if self._debug: self._debug_dict('actions block', actions) return actions def _parse_blk_trans_r(self, dic): """parse the "trans_r" block""" key = self.key_trans_r if self.old_key_trans_r in dic: msg = '\"trans\" is deprecated, please use \"trans_read\"' self._log.warn(msg) dic[self.key_trans_r] = dic[self.old_key_trans_r] del dic[self.old_key_trans_r] trans_r = self._get_entry(dic, key, mandatory=False) if trans_r: trans_r = trans_r.copy() if self._debug: self._debug_dict('trans_r block', trans_r) return trans_r def _parse_blk_trans_w(self, dic): """parse the "trans_w" block""" trans_w = self._get_entry(dic, self.key_trans_w, mandatory=False) if trans_w: trans_w = trans_w.copy() if self._debug: self._debug_dict('trans_w block', trans_w) return trans_w def _parse_blk_variables(self, dic): """parse the "variables" block""" variables = self._get_entry(dic, self.key_variables, mandatory=False) if variables: variables = variables.copy() if self._debug: self._debug_dict('variables block', variables) return variables def _parse_blk_dynvariables(self, dic): """parse the "dynvariables" block""" dvariables = self._get_entry(dic, self.key_dvariables, mandatory=False) if dvariables: dvariables = dvariables.copy() if self._debug: self._debug_dict('dynvariables block', dvariables) return dvariables def _parse_blk_uservariables(self, dic, current): """parse the "uservariables" block""" uvariables = self._get_entry(dic, self.key_uvariables, mandatory=False) uvars = {} if not self._reloading and uvariables: try: for name, prompt in uvariables.items(): if name in current: # ignore if already defined if self._debug: self._dbg(f'ignore uservariables {name}') continue content = userinput(prompt, debug=self._debug) uvars[name] = content except KeyboardInterrupt as exc: raise YamlException('interrupted') from exc if uvars: uvars = uvars.copy() if self._debug: self._debug_dict('uservariables block', uvars) # save uservariables if uvars: try: self._save_uservariables(uvars) except YamlException: # ignore pass return uvars ######################################################## # parsing helpers ######################################################## def _template_include_entry(self): """template all "include" entries""" # import_actions new = [] entries = self.settings.get(self.key_import_actions, []) new = self._template_list(entries) if new: self.settings[self.key_import_actions] = new # import_configs entries = self.settings.get(self.key_import_configs, []) new = self._template_list(entries) if new: self.settings[self.key_import_configs] = new # import_variables entries = self.settings.get(self.key_import_variables, []) new = self._template_list(entries) if new: self.settings[self.key_import_variables] = new # profile's import for _, val in self.profiles.items(): entries = val.get(self.key_import_profile_dfs, []) new = self._template_list(entries) if new: val[self.key_import_profile_dfs] = new def _norm_actions(self, actions): """ ensure each action is either pre or post explicitely action entry of the form {action_key: (pre|post, action)} """ if not actions: return actions new = {} for k, val in actions.items(): if k in (self.action_pre, self.action_post): for key, action in val.items(): new[key] = (k, action) else: new[k] = (self.action_post, val) return new def _norm_profiles(self, profiles): """normalize profiles entries""" if not profiles: return profiles new = {} # loop through each profile for pro, entries in profiles.items(): if pro == self.key_all: msg = f'\"{self.key_all}\" is a special profile name, ' msg += 'consider renaming to avoid any issue.' self._log.warn(msg) if not pro: msg = 'empty profile name' self._log.warn(msg) continue if not entries: # no entries in profile dict continue # add "dotfiles:" entry if not present in local object if self.key_profile_dotfiles not in entries or \ entries[self.key_profile_dotfiles] is None: entries[self.key_profile_dotfiles] = [] # add "dotfiles:" entry if not present in yaml dict dict_pro = self._yaml_dict[self.key_profiles][pro] if self.key_profile_dotfiles not in dict_pro or \ dict_pro[self.key_profile_dotfiles] is None: dict_pro[self.key_profile_dotfiles] = [] new[pro] = entries return new def _norm_dotfile_chmod(self, entry): value = str(entry[self.key_dotfile_chmod]) if value == self.chmod_ignore: # is preserve return if len(value) < 3: # bad format err = f'bad format for chmod: {value}' self._log.err(err) raise YamlException(f'config content error: {err}') # check is valid value try: int(value) except Exception as exc: err = f'bad format for chmod: {value}' self._log.err(err) err = f'config content error: {err}' raise YamlException(err) from exc # normalize chmod value for chmodv in list(value): chmodint = int(chmodv) if chmodint < 0 or chmodint > 7: err = f'bad format for chmod: {value}' self._log.err(err) raise YamlException( f'config content error: {err}' ) # octal entry[self.key_dotfile_chmod] = int(value, 8) def _norm_dotfiles(self, dotfiles): """normalize and check dotfiles entries""" if not dotfiles: return dotfiles new = {} for k, val in dotfiles.items(): if self.key_dotfile_src not in val: # add 'src' as key' if not present val[self.key_dotfile_src] = k new[k] = val else: new[k] = val if self.old_key_trans_r in val: # fix deprecated trans key msg = f'{k} \"trans\" is deprecated, please use \"trans_read\"' self._log.warn(msg) val[self.key_trans_r] = val[self.old_key_trans_r] del val[self.old_key_trans_r] new[k] = val if self.key_dotfile_link not in val: # apply link value if undefined value = self.settings[self.key_settings_link_dotfile_default] val[self.key_dotfile_link] = value if self.key_dotfile_noempty not in val: # apply noempty if undefined value = self.settings.get(self.key_settings_noempty, False) val[self.key_dotfile_noempty] = value if self.key_dotfile_template not in val: # apply template if undefined value = self.settings.get(self.key_settings_template, True) val[self.key_dotfile_template] = value if self.key_dotfile_chmod in val: # validate value of chmod if defined self._norm_dotfile_chmod(val) return new def _add_variables(self, new, shell=False, template=True, prio=False): """ add new variables @shell: execute the variable through the shell @template: template the variable @prio: new takes priority over existing variables """ if not new: return # merge if prio: self.variables = self._merge_dict(new, self.variables) else: # clear existing variable for k in list(new.keys()): if k in self.variables.keys(): del new[k] # merge self.variables = self._merge_dict(self.variables, new) # ensure enriched variables are relative to this config self.variables = self._enrich_vars(self.variables, self._profile) # re-create the templater self._redefine_templater() if template: # rec resolve variables with new ones self._rec_resolve_variables(self.variables) if shell and new: # shell exec self._shell_exec_dvars(self.variables, keys=new.keys()) # re-create the templater self._redefine_templater() def _enrich_vars(self, variables, profile): """return enriched variables""" # add profile variable if profile: variables['profile'] = profile # add some more variables path = self.settings.get(self.key_settings_dotpath) path = self._norm_path(path) variables['_dotdrop_dotpath'] = path variables['_dotdrop_cfgpath'] = self._norm_path(self._path) path = self.settings.get(self.key_settings_workdir) path = self._norm_path(path) variables['_dotdrop_workdir'] = path return variables def _get_profile_included_item(self, keyitem): """recursively get included in profile""" profiles = [self._profile] + self._inc_profiles items = {} for profile in profiles: seen = [self._profile] i = self.__get_profile_included_item(profile, keyitem, seen) items = self._merge_dict(i, items) return items def __get_profile_included_item(self, profile, keyitem, seen): """recursively get included from profile""" items = {} if not profile or profile not in self.profiles.keys(): return items # considered profile entry pentry = self.profiles.get(profile) # recursively get from inherited profile for inherited_profile in pentry.get(self.key_profile_include, []): if inherited_profile == profile or inherited_profile in seen: raise YamlException('\"include\" loop') seen.append(inherited_profile) new = self.__get_profile_included_item(inherited_profile, keyitem, seen) if self._debug: msg = f'included {keyitem} from {inherited_profile}: {new}' self._dbg(msg) items.update(new) cur = pentry.get(keyitem, {}) return self._merge_dict(cur, items) def _resolve_profile_all(self): """resolve some other parts of the config""" # profile -> ALL for k, val in self.profiles.items(): dfs = val.get(self.key_profile_dotfiles, None) if not dfs: continue if self.key_all in dfs: if self._debug: self._dbg(f'add ALL to profile \"{k}\"') val[self.key_profile_dotfiles] = self.dotfiles.keys() def _resolve_profile_includes(self): """resolve elements included through other profiles""" for k, _ in self.profiles.items(): self._rec_resolve_profile_include(k) def _rec_resolve_profile_include(self, profile): """ recursively resolve include of other profiles's: * dotfiles * actions returns dotfiles, actions """ this_profile = self.profiles[profile] # considered profile content dotfiles = this_profile.get(self.key_profile_dotfiles, []) or [] actions = this_profile.get(self.key_profile_actions, []) or [] includes = this_profile.get(self.key_profile_include, []) or [] if not includes: # nothing to include return dotfiles, actions if self._debug: incs = ','.join(includes) self._dbg(f'{profile} includes {incs}') self._dbg(f'{profile} dotfiles before include: {dotfiles}') self._dbg(f'{profile} actions before include: {actions}') seen = [] for i in uniq_list(includes): if self._debug: self._dbg(f'resolving includes "{profile}" <- "{i}"') # ensure no include loop occurs if i in seen: raise YamlException('\"include loop\"') seen.append(i) # included profile even exists if i not in self.profiles.keys(): self._log.warn(f'include unknown profile: {i}') continue # recursive resolve if self._debug: self._dbg(f'recursively resolving includes for profile "{i}"') o_dfs, o_actions = self._rec_resolve_profile_include(i) # merge dotfile keys if self._debug: msg = f'Merging dotfiles {profile}' msg += f' <- {i}: {dotfiles} <- {o_dfs}' self._dbg(msg) dotfiles.extend(o_dfs) this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles) # merge actions keys if self._debug: msg = f'Merging actions {profile} ' msg += f'<- {i}: {actions} <- {o_actions}' self._dbg(msg) actions.extend(o_actions) this_profile[self.key_profile_actions] = uniq_list(actions) dotfiles = this_profile.get(self.key_profile_dotfiles, []) actions = this_profile.get(self.key_profile_actions, []) if self._debug: self._dbg(f'{profile} dotfiles after include: {dotfiles}') self._dbg(f'{profile} actions after include: {actions}') # since included items are resolved here # we can clear these include self.profiles[profile][self.key_profile_include] = [] return dotfiles, actions ######################################################## # handle imported entries ######################################################## def _import_variables(self): """import external variables from paths""" paths = self.settings.get(self.key_import_variables, None) if not paths: return None paths = self._resolve_paths(paths) newvars = {} for path in paths: if self._debug: self._dbg(f'import variables from {path}') var = self._import_sub(path, self.key_variables, mandatory=False) if self._debug: self._dbg(f'import dynvariables from {path}') dvar = self._import_sub(path, self.key_dvariables, mandatory=False) merged = self._merge_dict(dvar, var) self._rec_resolve_variables(merged) if dvar.keys(): self._shell_exec_dvars(merged, keys=dvar.keys()) self._clear_profile_vars(merged) newvars = self._merge_dict(newvars, merged) if self._debug: self._debug_dict('imported variables', newvars) return newvars def _import_actions(self): """import external actions from paths""" paths = self.settings.get(self.key_import_actions, None) if not paths: return paths = self._resolve_paths(paths) for path in paths: if self._debug: self._dbg(f'import actions from {path}') new = self._import_sub(path, self.key_actions, mandatory=False, patch_func=self._norm_actions) self.actions = self._merge_dict(new, self.actions) def _import_profiles_dotfiles(self): """import profile dotfiles""" for k, val in self.profiles.items(): imp = val.get(self.key_import_profile_dfs, None) if not imp: continue if self._debug: self._dbg(f'import dotfiles for profile {k}') paths = self._resolve_paths(imp) for path in paths: current = val.get(self.key_dotfiles, []) new = self._import_sub(path, self.key_dotfiles, mandatory=False) val[self.key_dotfiles] = new + current def _import_config(self, path): """import config from path""" if self._debug: self._dbg(f'import config from {path}') self._dbg(f'profile: {self._profile}') self._dbg(f'included profiles: {self._inc_profiles}') sub = CfgYaml(path, profile=self._profile, addprofiles=self._inc_profiles, debug=self._debug, imported_configs=self.imported_configs) # settings are ignored from external file # except for filter_file and func_file self.settings[Settings.key_func_file] += [ self._norm_path(func_file) for func_file in sub.settings[Settings.key_func_file] ] self.settings[Settings.key_filter_file] += [ self._norm_path(func_file) for func_file in sub.settings[Settings.key_filter_file] ] # merge top entries self.dotfiles = self._merge_dict(self.dotfiles, sub.dotfiles) self.profiles = self._merge_dict(self.profiles, sub.profiles, deep=True) self.actions = self._merge_dict(self.actions, sub.actions) self.trans_r = self._merge_dict(self.trans_r, sub.trans_r) self.trans_w = self._merge_dict(self.trans_w, sub.trans_w) self._clear_profile_vars(sub.variables) self.imported_configs.append(path) self.imported_configs += sub.imported_configs if self._debug: self._debug_dict('add import_configs var', sub.variables) self._add_variables(sub.variables, prio=True) def _import_configs(self): """import configs from external files""" # settings -> import_configs imp = self.settings.get(self.key_import_configs, None) if not imp: return paths = self._resolve_paths(imp) for path in paths: if path in self.imported_configs: err = f'{path} imported more than once in {self._path}' raise YamlException(err) self._import_config(path) def _import_sub(self, path, key, mandatory=False, patch_func=None): """ import the block "key" from "path" patch_func is applied to each element if defined """ if self._debug: self._dbg(f'import \"{key}\" from \"{path}\"') extdict = self._load_yaml(path) new = self._get_entry(extdict, key, mandatory=mandatory) if patch_func: if self._debug: self._dbg(f'calling patch: {patch_func}') new = patch_func(new) if not new and mandatory: err = f'no \"{key}\" imported from \"{path}\"' self._log.warn(err) raise YamlException(err) if self._debug: self._dbg(f'imported \"{key}\": {new}') return new ######################################################## # add/remove entries ######################################################## def _new_profile(self, key): """add a new profile if it doesn't exist""" if key == self.key_all: err = f'profile key \"{key}\" is reserved' self._log.warn(err) raise YamlException(err) if not key: err = 'empty profile key' self._log.warn(err) raise YamlException(err) if key not in self.profiles.keys(): # update yaml_dict self._yaml_dict[self.key_profiles][key] = { self.key_profile_dotfiles: [] } self.profiles[key] = { self.key_profile_dotfiles: [] } if self._debug: self._dbg(f'adding new profile: {key}') self._dirty = True ######################################################## # handle deprecated entries ######################################################## def _fix_deprecated(self, yamldict): """fix deprecated entries""" if not yamldict: return self._fix_deprecated_link_by_default(yamldict) self._fix_deprecated_dotfile_link(yamldict) def _fix_deprecated_link_by_default(self, yamldict): """fix deprecated link_by_default""" old_key = 'link_by_default' newkey = self.key_imp_link if self.key_settings not in yamldict: return if not yamldict[self.key_settings]: return config = yamldict[self.key_settings] if old_key not in config: return if config[old_key]: config[newkey] = self.lnk_link else: config[newkey] = self.lnk_nolink del config[old_key] self._log.warn('deprecated \"link_by_default\"') self._dirty = True self._dirty_deprecated = True def _fix_deprecated_dotfile_link(self, yamldict): """fix deprecated link in dotfiles""" old_key = 'link_children' if self.key_dotfiles not in yamldict: return if not yamldict[self.key_dotfiles]: return for _, dotfile in yamldict[self.key_dotfiles].items(): if self.key_dotfile_link in dotfile and \ isinstance(dotfile[self.key_dotfile_link], bool): # patch link: cur = dotfile[self.key_dotfile_link] new = self.lnk_nolink if cur: new = self.lnk_link dotfile[self.key_dotfile_link] = new self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link: \"' warn += f', updated to \"link: {new}\"' self._log.warn(warn) if self.key_dotfile_link in dotfile and \ dotfile[self.key_dotfile_link] == self.lnk_link: # patch "link: link" # to "link: absolute" new = self.lnk_absolute dotfile[self.key_dotfile_link] = new self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link: link\"' warn += f', updated to \"link: {new}\"' self._log.warn(warn) if old_key in dotfile and \ isinstance(dotfile[old_key], bool): # patch link_children: cur = dotfile[old_key] new = self.lnk_nolink if cur: new = self.lnk_children del dotfile[old_key] dotfile[self.key_dotfile_link] = new self._dirty = True self._dirty_deprecated = True warn = 'deprecated \"link_children\" value' warn += f', updated to \"{new}\"' self._log.warn(warn) ######################################################## # yaml utils ######################################################## def _prepare_to_save(self, content): content = self._clear_none(content) # make sure we have the base entries if self.key_settings not in content: content[self.key_settings] = None if self.key_dotfiles not in content: content[self.key_dotfiles] = None if self.key_profiles not in content: content[self.key_profiles] = None return content def _load_yaml(self, path): """load a yaml file to a dict""" content = {} if self._debug: self._dbg(f'----------dump:{path}----------') cfg = '\n' with open(path, 'r', encoding='utf8') as file: for line in file: cfg += line self._dbg(cfg.rstrip()) self._dbg(f'----------end:{path}----------') try: content, fmt = self._yaml_load(path) self._config_format = fmt except Exception as exc: self._log.err(exc) err = f'config yaml error: {path}' raise YamlException(err) from exc if self._debug: self._dbg(f'format: {self._config_format}') return content def _validate(self, yamldict): """validate entries""" if not yamldict: return # check top entries for entry in self.top_entries: if entry not in yamldict: err = f'no {entry} entry found' self._log.err(err) raise YamlException(f'config format error: {err}') # check link_dotfile_default if self.key_settings not in yamldict: # no configs top entry return if not yamldict[self.key_settings]: # configs empty return # check settings values settings = yamldict[self.key_settings] if self.key_settings_link_dotfile_default not in settings: return val = settings[self.key_settings_link_dotfile_default] if val not in self.allowed_link_val: err = f'bad link value: {val}' self._log.err(err) self._log.err(f'allowed: {self.allowed_link_val}') raise YamlException(f'config content error: {err}') @classmethod def _yaml_load(cls, path): """load config file""" is_toml = path.lower().endswith(".toml") if is_toml: return cls.__toml_load(path), 'toml' return cls.__yaml_load(path), 'yaml' @classmethod def __yaml_load(cls, path): """load from yaml""" with open(path, 'r', encoding='utf8') as file: data = yaml() data.typ = 'rt' content = data.load(file) return content @classmethod def __toml_load(cls, path): """load from toml""" with open(path, 'r', encoding='utf8') as file: data = file.read() content = toml.loads(data) # handle inexistent dotfiles/profiles # since toml doesn't have a nul/nil/null/none if cls.key_dotfiles not in content: content[cls.key_dotfiles] = None if cls.key_profiles not in content: content[cls.key_profiles] = None return content @classmethod def _yaml_dump(cls, content, file, fmt='yaml'): """dump config file""" if fmt == 'toml': return cls.__toml_dump(content, file) if fmt == 'yaml': return cls.__yaml_dump(content, file) raise YamlException("unsupported format") @classmethod def __yaml_dump(cls, content, file): """dump to yaml""" data = yaml() data.default_flow_style = False data.indent = 2 data.typ = 'rt' data.dump(content, file) @classmethod def __toml_dump(cls, content, file): """dump to toml""" toml.dump(content, file) ######################################################## # templating ######################################################## def _redefine_templater(self): """create templater based on current variables""" fufile = None fifile = None if Settings.key_func_file in self.settings: fufile = self.settings[Settings.key_func_file] if Settings.key_filter_file in self.settings: fifile = self.settings[Settings.key_filter_file] self._tmpl = Templategen(variables=self.variables, func_file=fufile, filter_file=fifile) def _template_item(self, item, exc_if_fail=True): """ template an item using the templategen will raise an exception if template failed and exc_if_fail """ if not Templategen.var_is_template(item): return item try: val = item while Templategen.var_is_template(val): val = self._tmpl.generate_string(val) except UndefinedException as exc: if exc_if_fail: raise exc return val def _template_list(self, entries): """template a list of entries""" new = [] if not entries: return new for entry in entries: newe = self._template_item(entry) if self._debug and entry != newe: self._dbg(f'resolved: {entry} -> {newe}') new.append(newe) return new def _template_dict(self, entries): """template a dictionary of entries""" new = {} if not entries: return new for k, val in entries.items(): newv = self._template_item(val) if self._debug and val != newv: self._dbg(f'resolved: {val} -> {newv}') new[k] = newv return new def _template_dotfiles_entries(self): """template dotfiles entries""" if self._debug: self._dbg('templating dotfiles entries') dotfiles = self.dotfiles.copy() # make sure no dotfiles path is None for dotfile in dotfiles.values(): src = dotfile[self.key_dotfile_src] if src is None: dotfile[self.key_dotfile_src] = '' dst = dotfile[self.key_dotfile_dst] if dst is None: dotfile[self.key_dotfile_dst] = '' # resolve links before taking subset of # dotfiles to avoid issues in upper layer for dotfile in dotfiles.values(): # link if self.key_dotfile_link in dotfile: # normalize the link value link = dotfile[self.key_dotfile_link] newlink = self._resolve_dotfile_link(link) dotfile[self.key_dotfile_link] = newlink # only keep dotfiles related to the selected profile pdfs = [] pro = self.profiles.get(self._profile, []) if pro: pdfs = list(pro.get(self.key_profile_dotfiles, [])) # and any included profiles for addpro in self._inc_profiles: pro = self.profiles.get(addpro, []) if not pro: continue pdfsalt = pro.get(self.key_profile_dotfiles, []) pdfs.extend(pdfsalt) pdfs = uniq_list(pdfs) # if ALL is defined if self.key_all not in pdfs: # take a subset of the dotfiles newdotfiles = {} for k, val in dotfiles.items(): if k in pdfs: newdotfiles[k] = val dotfiles = newdotfiles for dotfile in dotfiles.values(): # src src = dotfile[self.key_dotfile_src] newsrc = self.resolve_dotfile_src(src, templater=self._tmpl) dotfile[self.key_dotfile_src] = newsrc # dst dst = dotfile[self.key_dotfile_dst] newdst = self.resolve_dotfile_dst(dst, templater=self._tmpl) dotfile[self.key_dotfile_dst] = newdst def _rec_resolve_variables(self, variables): """recursive resolve variables""" var = self._enrich_vars(variables, self._profile) # use a separated templategen to handle variables # resolved outside the main config func_files = self.settings[Settings.key_func_file] filter_files = self.settings[Settings.key_filter_file] templ = Templategen(variables=var, func_file=func_files, filter_file=filter_files) for k in variables.keys(): val = variables[k] while Templategen.var_is_template(val): val = templ.generate_string(val) variables[k] = val templ.update_variables(variables) if variables is self.variables: self._redefine_templater() def _get_profile_included_vars(self): """ resolve profile included variables/dynvariables returns inc_profiles, inc_var, inc_dvar """ for _, val in self.profiles.items(): if self.key_profile_include in val and \ val[self.key_profile_include]: new = [] for entry in val[self.key_profile_include]: new.append(self._tmpl.generate_string(entry)) val[self.key_profile_include] = new # now get the included ones pro_var = self._get_profile_included_item(self.key_profile_variables) pro_dvar = self._get_profile_included_item(self.key_profile_dvariables) # the included profiles inc_profiles = [] if self._profile and self._profile in self.profiles: pentry = self.profiles.get(self._profile) inc_profiles = pentry.get(self.key_profile_include, []) # exec incl dynvariables return inc_profiles, pro_var, pro_dvar ######################################################## # helpers ######################################################## def _clear_profile_vars(self, dic): """ remove profile variables from dic if found inplace to avoid profile variables being overwriten """ if not dic: return for k in self._profilevarskeys: dic.pop(k, None) def _parse_extended_import_path(self, path_entry): """Parse an import path in a tuple (path, fatal_not_found).""" if self._debug: self._dbg(f'parsing path entry {path_entry}') path, _, attribute = path_entry.rpartition(self.key_import_sep) fatal_not_found = attribute != self.key_import_ignore_key is_valid_attribute = attribute in ('', self.key_import_ignore_key) if not is_valid_attribute: # If attribute is not valid it can mean that: # - path_entry doesn't contain the separator, and attribute is set # to the whole path by str.rpartition # - path_entry contains a separator, but it's in the file path, so # attribute is set to whatever comes after the separator by # str.rpartition # In both cases, path_entry is the path we're looking for. if self._debug: msg = 'using attribute default values' msg += f' for path {path_entry}' self._dbg(msg) path = path_entry fatal_not_found = self.key_import_fatal_not_found elif self._debug: msg = f'path entry {path_entry} has fatal_not_found' msg += f' flag set to {fatal_not_found}' self._dbg(msg) return path, fatal_not_found def _handle_non_existing_path(self, path, fatal_not_found=True): """Raise an exception or log a warning to handle non-existing paths.""" error = f'bad path {path}' if fatal_not_found: raise YamlException(error) self._log.warn(error) def _check_path_existence(self, path, fatal_not_found=True): """Check if a path exists, raising if necessary.""" if os.path.exists(path): if self._debug: self._dbg(f'path {path} exists') return path self._handle_non_existing_path(path, fatal_not_found) # Explicit return for readability. Anything evaluating to false is ok. return None def _process_path(self, path_entry): """ This method processed a path entry. Namely it: - Normalizes the path. - Expands globs. - Checks for path existence, taking in account fatal_not_found. This method always returns a list containing only absolute paths existing on the filesystem. If the input is not a glob, the list contains at most one element, otherwise it could hold more. """ path, fatal_not_found = self._parse_extended_import_path(path_entry) path = self._norm_path(path) paths = self._glob_path(path) if self._is_glob(path) else [path] if not paths: if self._debug: self._dbg(f"glob path {path} didn't expand") self._handle_non_existing_path(path, fatal_not_found) return [] checked_paths = (self._check_path_existence(p, fatal_not_found) for p in paths) return [p for p in checked_paths if p] def _resolve_paths(self, paths): """ This function resolves a list of paths. This means normalizing, expanding globs and checking for existence, taking in account fatal_not_found flags. """ processed_paths = (self._process_path(p) for p in paths) return list(chain.from_iterable(processed_paths)) @classmethod def _merge_dict(cls, high, low, deep=False): """ both dict must be the same form/type if deep is True, then merge recursively """ if not high: high = {} if not low: low = {} if not high and not low: return {} if not deep: return {**low, **high} final = high.copy() for k, val in low.items(): if isinstance(val, dict): # content is dict, recurse if k not in final: final[k] = {} final[k] = cls._merge_dict(val, final[k], deep=True) elif isinstance(val, list): # content is list, merge if k not in final: final[k] = [] final[k] += val else: # don't know how to handle err = 'unable to merge' raise YamlException(err) return final @classmethod def _get_entry(cls, dic, key, mandatory=True): """return copy of entry from yaml dictionary""" if key not in dic: if mandatory: err = f'invalid config: no entry \"{key}\" found' raise YamlException(err) dic[key] = {} return deepcopy(dic[key]) if mandatory and not dic[key]: # ensure is not none dic[key] = {} return deepcopy(dic[key]) def _clear_none(self, dic): """recursively delete all none/empty values in a dictionary.""" new = {} for k, val in dic.items(): if k == self.key_dotfile_src: # allow empty dotfile src new[k] = val continue if k == self.key_dotfile_dst: # allow empty dotfile dst new[k] = val continue newv = val if isinstance(val, dict): # recursive travers dict newv = self._clear_none(val) if not newv: # no empty dict continue if newv is None: # no None value continue if isinstance(newv, list) and not newv: # no empty list continue new[k] = newv return new @classmethod def _is_glob(cls, path): """Quick test if path is a glob.""" return '*' in path or '?' in path def _glob_path(self, path): """Expand a glob.""" if self._debug: self._dbg(f'expanding glob {path}') expanded_path = os.path.expanduser(path) return glob.glob(expanded_path, recursive=True) def _norm_path(self, path): """Resolve a path either absolute or relative to config path""" if not path: return path path = os.path.expanduser(path) if not os.path.isabs(path): dirn = os.path.dirname(self._path) ret = os.path.join(dirn, path) if self._debug: msg = f'normalizing relative to cfg: {path} -> {ret}' self._dbg(msg) path = ret ret = os.path.normpath(path) if self._debug and path != ret: self._dbg(f'normalizing: {path} -> {ret}') return ret def _shell_exec_dvars(self, dic, keys=None): """shell execute dynvariables in-place""" if not keys: keys = dic.keys() for k in keys: val = dic[k] ret, out = shellrun(val, debug=self._debug) if not ret: err = f'var \"{k}: {val}\" failed: {out}' self._log.err(err) raise YamlException(err) if self._debug: self._dbg(f'{k}: `{val}` -> {out}') dic[k] = out @classmethod def _check_minversion(cls, minversion): if not minversion: return try: cur = ([int(x) for x in VERSION.split('.')]) cfg = ([int(x) for x in minversion.split('.')]) except Exception as exc: err = f'bad version: \"{VERSION}\" VS \"{minversion}\"' raise YamlException(err) from exc if cur < cfg: err = 'current dotdrop version is too old for that config file.' err += ' Please update.' raise YamlException(err) def _debug_entries(self): """debug print all interesting entries""" if not self._debug: return self._dbg('Current entries') self._debug_dict('entry settings', self.settings) self._debug_dict('entry dotfiles', self.dotfiles) self._debug_dict('entry profiles', self.profiles) self._debug_dict('entry actions', self.actions) self._debug_dict('entry trans_r', self.trans_r) self._debug_dict('entry trans_w', self.trans_w) self._debug_dict('entry variables', self.variables) def _debug_dict(self, title, elems): """pretty print dict""" if not self._debug: return self._dbg(f'{title}:') if not elems: return for k, val in elems.items(): if isinstance(val, dict): self._dbg(f' - \"{k}\"') for subkey, sub in val.items(): self._dbg(f' * {subkey}: \"{sub}\"') else: self._dbg(f' - \"{k}\": {val}') def _dbg(self, content): directory = os.path.basename(os.path.dirname(self._path)) pre = os.path.join(directory, os.path.basename(self._path)) self._log.dbg(f'[{pre}] {content}') def _save_uservariables(self, uvars): """save uservariables to file""" parent = os.path.dirname(self._path) # find a unique path path = None cnt = 0 while True: if cnt == 0: name = self.save_uservariables_name.format('') else: name = self.save_uservariables_name.format(f'-{cnt}') cnt += 1 path = os.path.join(parent, name) if not os.path.exists(path): break # save the config content = {'variables': uvars} try: if self._debug: self._dbg(f'saving uservariables values to {path}') with open(path, 'w', encoding='utf8') as file: self._yaml_dump(content, file, fmt=self._config_format) except Exception as exc: # self._log.err(exc) err = f'error saving uservariables to {path}' self._log.err(err) raise YamlException(err) from exc self._log.log(f'uservariables values saved to {path}') dotdrop-1.12.9/dotdrop/comparator.py000066400000000000000000000160541436430751200174660ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 handle the comparison of two dotfiles """ import os import filecmp # local imports from dotdrop.logger import Logger from dotdrop.utils import must_ignore, uniq_list, diff, \ get_file_perm class Comparator: """compare dotfiles helper""" def __init__(self, diff_cmd='', debug=False, ignore_missing_in_dotdrop=False): """constructor @diff_cmd: diff command to use @debug: enable debug """ self.diff_cmd = diff_cmd self.debug = debug self.log = Logger(debug=self.debug) self.ignore_missing_in_dotdrop = ignore_missing_in_dotdrop def compare(self, local_path, deployed_path, ignore=None, mode=None): """ diff local_path (dotdrop dotfile) and deployed_path (destination file) If mode is None, rights will be read from local_path """ if not ignore: ignore = [] local_path = os.path.expanduser(local_path) deployed_path = os.path.expanduser(deployed_path) self.log.dbg(f'comparing {local_path} and {deployed_path}') self.log.dbg(f'ignore pattern(s): {ignore}') # test type of file if os.path.isdir(local_path) and not os.path.isdir(deployed_path): ret = f'\"{local_path}\" is a dir' ret += f' while \"{deployed_path}\" is a file\n' return ret if not os.path.isdir(local_path) and os.path.isdir(deployed_path): ret = f'\"{local_path}\" is a file' ret += f' while \"{deployed_path}\" is a dir\n' return ret # test content if not os.path.isdir(local_path): self.log.dbg(f'{local_path} is a file') ret = self._comp_file(local_path, deployed_path, ignore) if not ret: ret = self._comp_mode(local_path, deployed_path, mode=mode) return ret self.log.dbg(f'{local_path} is a directory') ret = self._comp_dir(local_path, deployed_path, ignore) if not ret: ret = self._comp_mode(local_path, deployed_path, mode=mode) return ret def _comp_mode(self, local_path, deployed_path, mode=None): """ compare mode If mode is None, rights will be read on local_path """ local_mode = mode if not local_mode: local_mode = get_file_perm(local_path) deployed_mode = get_file_perm(deployed_path) if local_mode == deployed_mode: return '' msg = f'mode differ {local_path} ({local_mode:o}) ' msg += f'and {deployed_path} ({deployed_mode:o})' self.log.dbg(msg) ret = f'modes differ for {deployed_path} ' ret += f'({deployed_mode:o}) vs {local_mode:o}\n' return ret def _comp_file(self, local_path, deployed_path, ignore): """compare a file""" self.log.dbg(f'compare file {local_path} with {deployed_path}') if (self.ignore_missing_in_dotdrop and not os.path.exists(local_path)) \ or must_ignore([local_path, deployed_path], ignore, debug=self.debug): self.log.dbg(f'ignoring diff {local_path} and {deployed_path}') return '' return self._diff(local_path, deployed_path) def _comp_dir(self, local_path, deployed_path, ignore): """compare a directory""" self.log.dbg(f'compare directory {local_path} with {deployed_path}') if not os.path.exists(deployed_path): return '' if (self.ignore_missing_in_dotdrop and not os.path.exists(local_path)) \ or must_ignore([local_path, deployed_path], ignore, debug=self.debug): self.log.dbg(f'ignoring diff {local_path} and {deployed_path}') return '' if not os.path.isdir(deployed_path): return f'\"{deployed_path}\" is a file\n' return self._compare_dirs(local_path, deployed_path, ignore) def _compare_dirs(self, local_path, deployed_path, ignore): """compare directories""" self.log.dbg(f'compare {local_path} and {deployed_path}') ret = [] comp = filecmp.dircmp(local_path, deployed_path) # handle files only in deployed dir self.log.dbg(f'files only in deployed dir: {comp.left_only}') for i in comp.left_only: if self.ignore_missing_in_dotdrop or \ must_ignore([os.path.join(local_path, i)], ignore, debug=self.debug): continue ret.append(f'=> \"{i}\" does not exist on destination\n') # handle files only in dotpath dir self.log.dbg(f'files only in dotpath dir: {comp.right_only}') for i in comp.right_only: if must_ignore([os.path.join(deployed_path, i)], ignore, debug=self.debug): continue if not self.ignore_missing_in_dotdrop: ret.append(f'=> \"{i}\" does not exist in dotdrop\n') # same local_path and deployed_path but different type funny = comp.common_funny self.log.dbg(f'files with different types: {funny}') for i in funny: source_file = os.path.join(local_path, i) deployed_file = os.path.join(deployed_path, i) if self.ignore_missing_in_dotdrop and \ not os.path.exists(source_file): continue if must_ignore([source_file, deployed_file], ignore, debug=self.debug): continue short = os.path.basename(source_file) # file vs dir ret.append(f'=> different type: \"{short}\"\n') # content is different funny = comp.diff_files funny.extend(comp.funny_files) funny = uniq_list(funny) self.log.dbg(f'files with different content: {funny}') for i in funny: source_file = os.path.join(local_path, i) deployed_file = os.path.join(deployed_path, i) if self.ignore_missing_in_dotdrop and \ not os.path.exists(source_file): continue if must_ignore([source_file, deployed_file], ignore, debug=self.debug): continue ret.append(self._diff(source_file, deployed_file, header=True)) # recursively compare subdirs for i in comp.common_dirs: sublocal_path = os.path.join(local_path, i) subdeployed_path = os.path.join(deployed_path, i) ret.extend(self._comp_dir(sublocal_path, subdeployed_path, ignore)) return ''.join(ret) def _diff(self, local_path, deployed_path, header=False): """diff two files""" out = diff(modified=local_path, original=deployed_path, diff_cmd=self.diff_cmd, debug=self.debug) if header: lshort = os.path.basename(local_path) out = f'=> diff \"{lshort}\":\n{out}' return out dotdrop-1.12.9/dotdrop/dictparser.py000066400000000000000000000016111436430751200174500ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 dictionary parser abstract class """ from dotdrop.logger import Logger class DictParser: """a dict parser""" log = Logger() @classmethod def _adjust_yaml_keys(cls, value): """adjust value for object 'cls'""" return value @classmethod def parse(cls, key, value): """parse (key,value) and construct object 'cls'""" tmp = value try: tmp = value.copy() except AttributeError: pass newv = cls._adjust_yaml_keys(tmp) if not key: return cls(**newv) return cls(key=key, **newv) @classmethod def parse_dict(cls, items): """parse a dictionary and construct object 'cls'""" if not items: return [] return [cls.parse(k, v) for k, v in items.items()] dotdrop-1.12.9/dotdrop/dotdrop.py000066400000000000000000000722511436430751200167730ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 entry point """ import os import sys import time import fnmatch from concurrent import futures # local imports from dotdrop.options import Options from dotdrop.logger import Logger from dotdrop.templategen import Templategen from dotdrop.installer import Installer from dotdrop.updater import Updater from dotdrop.comparator import Comparator from dotdrop.importer import Importer from dotdrop.utils import get_tmpdir, removepath, \ uniq_list, patch_ignores, dependencies_met, \ adapt_workers, check_version, pivot_path from dotdrop.linktypes import LinkTypes from dotdrop.exceptions import YamlException, \ UndefinedException, UnmetDependency LOG = Logger() TRANS_SUFFIX = 'trans' ########################################################### # entry point ########################################################### def action_executor(opts, actions, defactions, templater, post=False): """closure for action execution""" def execute(): """ execute actions and return True, None if ok False, errstring if issue """ actiontype = 'pre' if not post else 'post' # execute default actions for action in defactions: if opts.dry: LOG.dry(f'would execute def-{actiontype}-action: {action}') continue LOG.dbg(f'executing def-{actiontype}-action: {action}') ret = action.execute(templater=templater, debug=opts.debug) if not ret: err = f'def-{actiontype}-action \"{action.key}\" failed' LOG.err(err) return False, err # execute actions for action in actions: if opts.dry: err = f'would execute {actiontype}-action: {action}' LOG.dry(err) continue LOG.dbg(f'executing {actiontype}-action: {action}') ret = action.execute(templater=templater, debug=opts.debug) if not ret: err = f'{actiontype}-action \"{action.key}\" failed' LOG.err(err) return False, err return True, None return execute def _dotfile_update(opts, path, key=False): """ update a dotfile pointed by path if key is false or by key (in path) """ updater = Updater(opts.dotpath, opts.variables, opts.conf, opts.profile, dry=opts.dry, safe=opts.safe, debug=opts.debug, ignore=opts.update_ignore, showpatch=opts.update_showpatch, ignore_missing_in_dotdrop=opts.ignore_missing_in_dotdrop) if key: return updater.update_key(path) return updater.update_path(path) def _dotfile_compare(opts, dotfile, tmp): """ compare a dotfile returns True if same """ templ = _get_templater(opts) ignore_missing_in_dotdrop = opts.ignore_missing_in_dotdrop or \ dotfile.ignore_missing_in_dotdrop inst = Installer(create=opts.create, backup=opts.backup, dry=opts.dry, base=opts.dotpath, workdir=opts.workdir, debug=opts.debug, backup_suffix=opts.install_backup_suffix, diff_cmd=opts.diff_command) comp = Comparator(diff_cmd=opts.diff_command, debug=opts.debug, ignore_missing_in_dotdrop=ignore_missing_in_dotdrop) # add dotfile variables newvars = dotfile.get_dotfile_variables() templ.add_tmp_vars(newvars=newvars) # dotfiles does not exist / not installed LOG.dbg(f'comparing {dotfile}') src = dotfile.src if not os.path.lexists(os.path.expanduser(dotfile.dst)): line = f'=> compare {dotfile.key}: \"{dotfile.dst}\" ' line += 'does not exist on destination' LOG.log(line) return False # apply transformation tmpsrc = None if dotfile.trans_r: LOG.dbg('applying transformation before comparing') tmpsrc = apply_trans(opts.dotpath, dotfile, templ, debug=opts.debug) if not tmpsrc: # could not apply trans return False src = tmpsrc # is a symlink pointing to itself asrc = os.path.join(opts.dotpath, os.path.expanduser(src)) adst = os.path.expanduser(dotfile.dst) if os.path.samefile(asrc, adst): line = f'=> compare {dotfile.key}: diffing with \"{dotfile.dst}\"' LOG.dbg(line) LOG.dbg('points to itself') return True ignores = list(set(opts.compare_ignore + dotfile.cmpignore)) ignores = patch_ignores(ignores, dotfile.dst, debug=opts.debug) insttmp = None if dotfile.template and Templategen.is_template(src, ignore=ignores, debug=opts.debug): # install dotfile to temporary dir for compare ret, err, insttmp = inst.install_to_temp(templ, tmp, src, dotfile.dst, is_template=True, chmod=dotfile.chmod, set_create=True) if not ret: # failed to install to tmp line = f'=> compare {dotfile.key} error: {err}' LOG.log(line) LOG.err(err) return False src = insttmp # compare # need to be executed before cleaning diff = comp.compare(src, dotfile.dst, ignore=ignores, mode=dotfile.chmod) # clean tmp transformed dotfile if any if tmpsrc: tmpsrc = os.path.join(opts.dotpath, tmpsrc) if os.path.exists(tmpsrc): removepath(tmpsrc, LOG) # clean tmp template dotfile if any if insttmp and os.path.exists(insttmp): removepath(insttmp, LOG) if diff != '': # print diff results if opts.compare_fileonly: line = f'=> differ: \"{dotfile.key}\" \"{dotfile.dst}\"' LOG.log(line) else: line = f'=> compare {dotfile.key}: diffing with \"{dotfile.dst}\"' LOG.log(line) LOG.emph(diff) return False # no difference line = f'=> compare {dotfile.key}: diffing with \"{dotfile.dst}\"' LOG.dbg(line) LOG.dbg('same file') return True def _dotfile_install(opts, dotfile, tmpdir=None): """ install a dotfile returns """ # installer inst = _get_install_installer(opts, tmpdir=tmpdir) # templater templ = _get_templater(opts) # add dotfile variables newvars = dotfile.get_dotfile_variables() templ.add_tmp_vars(newvars=newvars) preactions = [] if not opts.install_temporary: preactions.extend(dotfile.get_pre_actions()) defactions = opts.install_default_actions_pre pre_actions_exec = action_executor(opts, preactions, defactions, templ, post=False) LOG.dbg(f'installing dotfile: \"{dotfile.key}\"') LOG.dbg(dotfile.prt()) ignores = list(set(opts.install_ignore + dotfile.instignore)) ignores = patch_ignores(ignores, dotfile.dst, debug=opts.debug) is_template = dotfile.template and Templategen.is_template( dotfile.src, ignore=ignores, ) if hasattr(dotfile, 'link') and dotfile.link in ( LinkTypes.LINK, LinkTypes.LINK_CHILDREN, LinkTypes.RELATIVE, LinkTypes.ABSOLUTE ): # nolink|relative|absolute|link_children ret, err = inst.install(templ, dotfile.src, dotfile.dst, dotfile.link, actionexec=pre_actions_exec, is_template=is_template, ignore=ignores, chmod=dotfile.chmod, force_chmod=opts.install_force_chmod) else: # nolink src = dotfile.src tmp = None if dotfile.trans_r: tmp = apply_trans(opts.dotpath, dotfile, templ, debug=opts.debug) if not tmp: return False, dotfile.key, None src = tmp # make sure to re-evaluate if is template is_template = dotfile.template and Templategen.is_template( src, ignore=ignores, ) ret, err = inst.install(templ, src, dotfile.dst, LinkTypes.NOLINK, actionexec=pre_actions_exec, noempty=dotfile.noempty, ignore=ignores, is_template=is_template, chmod=dotfile.chmod, force_chmod=opts.install_force_chmod) if tmp: tmp = os.path.join(opts.dotpath, tmp) if os.path.exists(tmp): removepath(tmp, LOG) # check result of installation if ret: # dotfile was installed if not opts.install_temporary: defactions = opts.install_default_actions_post postactions = dotfile.get_post_actions() post_actions_exec = action_executor(opts, postactions, defactions, templ, post=True) post_actions_exec() else: # dotfile was NOT installed if opts.install_force_action: # pre-actions LOG.dbg('force pre action execution ...') pre_actions_exec() # post-actions LOG.dbg('force post action execution ...') defactions = opts.install_default_actions_post postactions = dotfile.get_post_actions() post_actions_exec = action_executor(opts, postactions, defactions, templ, post=True) post_actions_exec() return ret, dotfile.key, err def cmd_install(opts): """install dotfiles for this profile""" dotfiles = opts.dotfiles prof = opts.conf.get_profile() adapt_workers(opts, LOG) pro_pre_actions = prof.get_pre_actions() if prof else [] pro_post_actions = prof.get_post_actions() if prof else [] if opts.install_keys: # filtered dotfiles to install uniq = uniq_list(opts.install_keys) dotfiles = [d for d in dotfiles if d.key in uniq] if not dotfiles: msg = f'no dotfile to install for this profile (\"{opts.profile}\")' LOG.warn(msg) return False lfs = [k.key for k in dotfiles] LOG.dbg(f'dotfiles registered for install: {lfs}') # the installer tmpdir = None if opts.install_temporary: tmpdir = get_tmpdir() installed = [] # clear the workdir if opts.install_clear_workdir and not opts.dry: LOG.dbg(f'clearing the workdir under {opts.workdir}') for root, _, files in os.walk(opts.workdir): for file in files: fpath = os.path.join(root, file) removepath(fpath, logger=LOG) # execute profile pre-action LOG.dbg(f'run {len(pro_pre_actions)} profile pre actions') templ = _get_templater(opts) ret, _ = action_executor(opts, pro_pre_actions, [], templ, post=False)() if not ret: return False # install each dotfile if opts.workers > 1: # in parallel LOG.dbg(f'run with {opts.workers} workers') ex = futures.ThreadPoolExecutor(max_workers=opts.workers) wait_for = [] for dotfile in dotfiles: j = ex.submit(_dotfile_install, opts, dotfile, tmpdir=tmpdir) wait_for.append(j) # check result for fut in futures.as_completed(wait_for): tmpret, key, err = fut.result() # check result if tmpret: installed.append(key) elif err: LOG.err(f'installing \"{key}\" failed: {err}') else: # sequentially for dotfile in dotfiles: tmpret, key, err = _dotfile_install(opts, dotfile, tmpdir=tmpdir) # check result if tmpret: installed.append(key) elif err: LOG.err(f'installing \"{key}\" failed: {err}') # execute profile post-action if len(installed) > 0 or opts.install_force_action: msg = f'run {len(pro_post_actions)} profile post actions' LOG.dbg(msg) ret, _ = action_executor(opts, pro_post_actions, [], templ, post=False)() if not ret: return False insts = ','.join(installed) LOG.dbg(f'install done: installed \"{insts}\"') if opts.install_temporary: LOG.log(f'\ninstalled to tmp \"{tmpdir}\".') LOG.log(f'\n{len(installed)} dotfile(s) installed.') return True def _workdir_enum(opts): workdir_files = [] for root, _, files in os.walk(opts.workdir): for file in files: fpath = os.path.join(root, file) workdir_files.append(fpath) for dotfile in opts.dotfiles: src = os.path.join(opts.dotpath, dotfile.src) if dotfile.link == LinkTypes.NOLINK: # ignore not link files continue if not Templategen.is_template(src): # ignore not template continue newpath = pivot_path(dotfile.dst, opts.workdir, striphome=True, logger=None) if os.path.isdir(newpath): # recursive pattern = f'{newpath}/*' files = workdir_files.copy() for file in files: if fnmatch.fnmatch(file, pattern): workdir_files.remove(file) # only checks children children = [f.path for f in os.scandir(newpath)] for child in children: if child in workdir_files: workdir_files.remove(child) else: if newpath in workdir_files: workdir_files.remove(newpath) for wfile in workdir_files: line = f'=> \"{wfile}\" does not exist in dotdrop' LOG.log(line) return len(workdir_files) def cmd_compare(opts, tmp): """compare dotfiles and return True if all identical""" dotfiles = opts.dotfiles if not dotfiles: msg = f'no dotfile defined for this profile (\"{opts.profile}\")' LOG.warn(msg) return True # compare only specific files selected = dotfiles if opts.compare_focus: selected = _select(opts.compare_focus, dotfiles) if len(selected) < 1: LOG.log('\nno dotfile to compare') return False same = True cnt = 0 if opts.workers > 1: # in parallel LOG.dbg(f'run with {opts.workers} workers') ex = futures.ThreadPoolExecutor(max_workers=opts.workers) wait_for = [] for dotfile in selected: if not dotfile.src and not dotfile.dst: # ignore fake dotfile continue j = ex.submit(_dotfile_compare, opts, dotfile, tmp) wait_for.append(j) # check result for fut in futures.as_completed(wait_for): if not fut.result(): same = False cnt += 1 else: # sequentially for dotfile in selected: if not dotfile.src and not dotfile.dst: # ignore fake dotfile continue if not _dotfile_compare(opts, dotfile, tmp): same = False cnt += 1 if opts.compare_workdir and _workdir_enum(opts) > 0: same = False LOG.log(f'\n{cnt} dotfile(s) compared.') return same def cmd_update(opts): """update the dotfile(s) from path(s) or key(s)""" cnt = 0 paths = opts.update_path iskey = opts.update_iskey if opts.profile not in [p.key for p in opts.profiles]: LOG.err(f'no such profile \"{opts.profile}\"') return False adapt_workers(opts, LOG) if not paths: # update the entire profile if iskey: LOG.dbg(f'update by keys: {paths}') paths = [d.key for d in opts.dotfiles] else: LOG.dbg(f'update by paths: {paths}') paths = [d.dst for d in opts.dotfiles] msg = f'Update all dotfiles for profile \"{opts.profile}\"' if opts.safe and not LOG.ask(msg): LOG.log(f'\n{cnt} file(s) updated.') return False # check there's something to do if not paths: LOG.log('\nno dotfile to update') return True LOG.dbg(f'dotfile to update: {paths}') # update each dotfile if opts.workers > 1: # in parallel LOG.dbg(f'run with {opts.workers} workers') ex = futures.ThreadPoolExecutor(max_workers=opts.workers) wait_for = [] for path in paths: j = ex.submit(_dotfile_update, opts, path, key=iskey) wait_for.append(j) # check result for fut in futures.as_completed(wait_for): if fut.result(): cnt += 1 else: # sequentially for path in paths: if _dotfile_update(opts, path, key=iskey): cnt += 1 LOG.log(f'\n{cnt} file(s) updated.') return cnt == len(paths) def cmd_importer(opts): """import dotfile(s) from paths""" ret = True cnt = 0 paths = opts.import_path importer = Importer(opts.profile, opts.conf, opts.dotpath, opts.diff_command, opts.variables, dry=opts.dry, safe=opts.safe, debug=opts.debug, keepdot=opts.keepdot, ignore=opts.import_ignore) for path in paths: tmpret = importer.import_path(path, import_as=opts.import_as, import_link=opts.import_link, import_mode=opts.import_mode, import_transw=opts.import_transw, import_transr=opts.import_transr) if tmpret < 0: ret = False elif tmpret > 0: cnt += 1 if opts.dry: LOG.dry('new config file would be:') LOG.raw(opts.conf.dump()) else: opts.conf.save() LOG.log(f'\n{cnt} file(s) imported.') return ret def cmd_list_profiles(opts): """list all profiles""" LOG.emph('Available profile(s):\n') for profile in opts.profiles: if opts.profiles_grepable: fmt = f'{profile.key}' LOG.raw(fmt) else: LOG.sub(profile.key, end='') LOG.log(f' ({len(profile.dotfiles)} dotfiles)') LOG.log('') def cmd_files(opts): """list all dotfiles for a specific profile""" if opts.profile not in [p.key for p in opts.profiles]: LOG.warn(f'unknown profile \"{opts.profile}\"') return what = 'Dotfile(s)' if opts.files_templateonly: what = 'Template(s)' LOG.emph(f'{what} for profile \"{opts.profile}\":\n') for dotfile in opts.dotfiles: if opts.files_templateonly: src = os.path.join(opts.dotpath, dotfile.src) if not Templategen.is_template(src): continue if opts.files_grepable: fmt = f'{dotfile.key},' fmt += f'dst:{dotfile.dst},' fmt += f'src:{dotfile.src},' fmt += f'link:{dotfile.link.name.lower()}' if dotfile.chmod: fmt += f',chmod:{dotfile.chmod:o}' else: fmt += ',chmod:None' LOG.raw(fmt) else: LOG.log(f'{dotfile.key}', bold=True) LOG.sub(f'dst: {dotfile.dst}') LOG.sub(f'src: {dotfile.src}') LOG.sub(f'link: {dotfile.link.name.lower()}') if dotfile.chmod: LOG.sub(f'chmod: {dotfile.chmod:o}') LOG.log('') def cmd_detail(opts): """list details on all files for all dotfile entries""" if opts.profile not in [p.key for p in opts.profiles]: LOG.warn(f'unknown profile \"{opts.profile}\"') return dotfiles = opts.dotfiles if opts.detail_keys: # filtered dotfiles to install uniq = uniq_list(opts.details_keys) dotfiles = [d for d in dotfiles if d.key in uniq] LOG.emph(f'dotfiles details for profile \"{opts.profile}\":\n') for dotfile in dotfiles: _detail(opts.dotpath, dotfile) LOG.log('') def cmd_remove(opts): """remove dotfile from dotpath and from config""" paths = opts.remove_path iskey = opts.remove_iskey if not paths: LOG.log('no dotfile to remove') return False pathss = ','.join(paths) LOG.dbg(f'dotfile(s) to remove: {pathss}') removed = [] for key in paths: if not iskey: # by path dotfiles = opts.conf.get_dotfile_by_dst(key) if not dotfiles: LOG.warn(f'{key} ignored, does not exist') continue else: # by key dotfile = opts.conf.get_dotfile(key) if not dotfile: LOG.warn(f'{key} ignored, does not exist') continue dotfiles = [dotfile] for dotfile in dotfiles: k = dotfile.key # ignore if uses any type of link if dotfile.link != LinkTypes.NOLINK: msg = f'{k} uses symlink, remove manually' LOG.warn(msg) continue LOG.dbg(f'removing {key}') # make sure is part of the profile if dotfile.key not in [d.key for d in opts.dotfiles]: msg = f'{key} ignored, not associated to this profile' LOG.warn(msg) continue profiles = opts.conf.get_profiles_by_dotfile_key(k) pkeys = ','.join([p.key for p in profiles]) if opts.dry: LOG.dry(f'would remove {dotfile} from {pkeys}') continue msg = f'Remove \"{k}\" from all these profiles: {pkeys}' if opts.safe and not LOG.ask(msg): return False LOG.dbg(f'remove dotfile: {dotfile}') for profile in profiles: if not opts.conf.del_dotfile_from_profile(dotfile, profile): return False if not opts.conf.del_dotfile(dotfile): return False # remove dotfile from dotpath dtpath = os.path.join(opts.dotpath, dotfile.src) removepath(dtpath, LOG) # remove empty directory parent = os.path.dirname(dtpath) # remove any empty parent up to dotpath while parent != opts.dotpath: if os.path.isdir(parent) and not os.listdir(parent): msg = f'Remove empty dir \"{parent}\"' if opts.safe and not LOG.ask(msg): break removepath(parent, LOG) parent = os.path.dirname(parent) removed.append(dotfile) if opts.dry: LOG.dry('new config file would be:') LOG.raw(opts.conf.dump()) else: opts.conf.save() if removed: LOG.log('\nFollowing dotfile(s) are not tracked anymore:') entries = [f'- \"{r.dst}\" (was tracked as \"{r.key}\")' for r in removed] LOG.log('\n'.join(entries)) else: LOG.log('\nno dotfile removed') return True ########################################################### # helpers ########################################################### def _get_install_installer(opts, tmpdir=None): """get an installer instance for cmd_install""" inst = Installer(create=opts.create, backup=opts.backup, dry=opts.dry, safe=opts.safe, base=opts.dotpath, workdir=opts.workdir, diff=opts.install_diff, debug=opts.debug, totemp=tmpdir, showdiff=opts.install_showdiff, backup_suffix=opts.install_backup_suffix, diff_cmd=opts.diff_command) return inst def _get_templater(opts): """get an templater instance""" templ = Templategen(base=opts.dotpath, variables=opts.variables, func_file=opts.func_file, filter_file=opts.filter_file, debug=opts.debug) return templ def _detail(dotpath, dotfile): """display details on all files under a dotfile entry""" entry = f'{dotfile.key}' attribs = [] attribs.append(f'dst: \"{dotfile.dst}\"') attribs.append(f'link: \"{dotfile.link.name.lower()}\"') attribs.append(f'chmod: \"{dotfile.chmod}\"') attrs = ', '.join(attribs) LOG.log(f'{entry} ({attrs})') path = os.path.join(dotpath, os.path.expanduser(dotfile.src)) if not os.path.isdir(path): template = 'no' if dotfile.template and Templategen.is_template(path): template = 'yes' LOG.sub(f'{path} (template:{template})') else: for root, _, files in os.walk(path): for file in files: fpath = os.path.join(root, file) template = 'no' if dotfile.template and Templategen.is_template(fpath): template = 'yes' LOG.sub(f'{fpath} (template:{template})') def _select(selections, dotfiles): selected = [] for selection in selections: dotfile = next( (x for x in dotfiles if os.path.expanduser(x.dst) == os.path.expanduser(selection)), None ) if dotfile: selected.append(dotfile) else: LOG.err(f'no dotfile matches \"{selection}\"') return selected def apply_trans(dotpath, dotfile, templater, debug=False): """ apply the read transformation to the dotfile return None if fails and new source if succeed """ src = dotfile.src new_src = f'{src}.{TRANS_SUFFIX}' trans = dotfile.trans_r LOG.dbg(f'executing transformation: {trans}') srcpath = os.path.join(dotpath, src) temp = os.path.join(dotpath, new_src) if not trans.transform(srcpath, temp, templater=templater, debug=debug): msg = f'transformation \"{trans.key}\" failed for {dotfile.key}' LOG.err(msg) if new_src and os.path.exists(new_src): removepath(new_src, LOG) return None return new_src ########################################################### # main ########################################################### def _exec_command(opts): """execute command""" ret = True command = '' try: if opts.cmd_profiles: # list existing profiles command = 'profiles' LOG.dbg(f'running cmd: {command}') cmd_list_profiles(opts) elif opts.cmd_files: # list files for selected profile command = 'files' LOG.dbg(f'running cmd: {command}') cmd_files(opts) elif opts.cmd_install: # install the dotfiles stored in dotdrop command = 'install' LOG.dbg(f'running cmd: {command}') ret = cmd_install(opts) elif opts.cmd_compare: # compare local dotfiles with dotfiles stored in dotdrop command = 'compare' LOG.dbg(f'running cmd: {command}') tmp = get_tmpdir() ret = cmd_compare(opts, tmp) # clean tmp directory removepath(tmp, LOG) elif opts.cmd_import: # import dotfile(s) command = 'import' LOG.dbg(f'running cmd: {command}') ret = cmd_importer(opts) elif opts.cmd_update: # update a dotfile command = 'update' LOG.dbg(f'running cmd: {command}') ret = cmd_update(opts) elif opts.cmd_detail: # detail files command = 'detail' LOG.dbg(f'running cmd: {command}') cmd_detail(opts) elif opts.cmd_remove: # remove dotfile command = 'remove' LOG.dbg(f'running cmd: {command}') cmd_remove(opts) except UndefinedException as exc: LOG.err(exc) ret = False except KeyboardInterrupt: LOG.err('interrupted') ret = False return ret, command def main(): """entry point""" # check dependencies are met try: dependencies_met() except UnmetDependency as exc: LOG.err(exc) return False time0 = time.time() try: opts = Options() except YamlException as exc: LOG.err(f'config error: {exc}') return False except UndefinedException as exc: LOG.err(f'config error: {exc}') return False if opts.debug: LOG.debug = opts.debug LOG.dbg('\n\n') options_time = time.time() - time0 if opts.check_version: check_version() time0 = time.time() ret, command = _exec_command(opts) cmd_time = time.time() - time0 LOG.dbg(f'done executing command \"{command}\"') LOG.dbg(f'options loaded in {options_time}') LOG.dbg(f'command executed in {cmd_time}') if ret and opts.conf.save(): LOG.log('config file updated') LOG.dbg(f'return {ret}') return ret if __name__ == '__main__': if main(): sys.exit(0) sys.exit(1) dotdrop-1.12.9/dotdrop/dotfile.py000066400000000000000000000124301436430751200167370ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 represents a dotfile in dotdrop """ from dotdrop.linktypes import LinkTypes from dotdrop.dictparser import DictParser from dotdrop.action import Action class Dotfile(DictParser): """Represent a dotfile.""" # dotfile keys key_noempty = 'ignoreempty' key_trans_r = 'trans_read' key_trans_w = 'trans_write' key_template = 'template' def __init__(self, key, dst, src, actions=None, trans_r=None, trans_w=None, link=LinkTypes.NOLINK, noempty=False, cmpignore=None, upignore=None, instignore=None, template=True, chmod=None, ignore_missing_in_dotdrop=False): """ constructor @key: dotfile key @dst: dotfile dst (in user's home usually) @src: dotfile src (in dotpath) @actions: dictionary of actions to execute for this dotfile @trans_r: transformation to change dotfile before it is installed @trans_w: transformation to change dotfile before updating it @link: link behavior @noempty: ignore empty template if True @upignore: patterns to ignore when updating @cmpignore: patterns to ignore when comparing @instignore: patterns to ignore when installing @template: template this dotfile @chmod: file permission """ self.actions = actions or [] self.dst = dst self.key = key self.link = LinkTypes.get(link) self.noempty = noempty self.src = src self.trans_r = trans_r self.trans_w = trans_w self.upignore = upignore or [] self.cmpignore = cmpignore or [] self.instignore = instignore or [] self.template = template self.chmod = chmod self.ignore_missing_in_dotdrop = ignore_missing_in_dotdrop if self.link != LinkTypes.NOLINK and \ ( (trans_r and len(trans_r) > 0) or (trans_w and len(trans_w) > 0) ): msg = f'[{key}] transformations disabled' msg += ' because dotfile is linked' self.log.warn(msg) self.trans_r = [] self.trans_w = [] def get_dotfile_variables(self): """return this dotfile specific variables""" return { '_dotfile_abs_src': self.src, '_dotfile_abs_dst': self.dst, '_dotfile_key': self.key, '_dotfile_link': str(self.link), } def get_pre_actions(self): """return all 'pre' actions""" return [a for a in self.actions if a.kind == Action.pre] def get_post_actions(self): """return all 'post' actions""" return [a for a in self.actions if a.kind == Action.post] def get_trans_r(self): """return trans_r object""" return self.trans_r def get_trans_w(self): """return trans_w object""" return self.trans_w @classmethod def _adjust_yaml_keys(cls, value): """patch dict""" value['noempty'] = value.get(cls.key_noempty, False) value['trans_r'] = value.get(cls.key_trans_r) value['trans_w'] = value.get(cls.key_trans_w) value['template'] = value.get(cls.key_template, True) # remove old entries value.pop(cls.key_noempty, None) value.pop(cls.key_trans_r, None) value.pop(cls.key_trans_w, None) return value def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): return hash(self.dst) ^ hash(self.src) ^ hash(self.key) def __str__(self): msg = f'key:\"{self.key}\"' msg += f', src:\"{self.src}\"' msg += f', dst:\"{self.dst}\"' msg += f', link:\"{self.link}\"' msg += f', template:{self.template}' if self.chmod: if isinstance(self.chmod, int) or len(self.chmod) == 3: msg += f', chmod:{self.chmod:o}' else: msg += f', chmod:\"{self.chmod}\"' return msg def prt(self): """extended dotfile to str""" indent = ' ' out = f'dotfile: \"{self.key}\"' out += f'\n{indent}src: \"{self.src}\"' out += f'\n{indent}dst: \"{self.dst}\"' out += f'\n{indent}link: \"{self.link}\"' out += f'\n{indent}template: \"{self.template}\"' if self.chmod: if isinstance(self.chmod, int) or len(self.chmod) == 3: out += f'\n{indent}chmod: \"{self.chmod:o}\"' else: out += f'\n{indent}chmod: \"{self.chmod}\"' out += f'\n{indent}pre-action:' some = self.get_pre_actions() if some: for act in some: out += f'\n{2*indent}- {act}' out += f'\n{indent}post-action:' some = self.get_post_actions() if some: for act in some: out += f'\n{2*indent}- {act}' out += f'\n{indent}trans_r:' some = self.get_trans_r() if some: out += f'\n{2*indent}- {some}' out += f'\n{indent}trans_w:' some = self.get_trans_w() if some: out += f'\n{2*indent}- {some}' return out def __repr__(self): return f'dotfile({self})' dotdrop-1.12.9/dotdrop/exceptions.py000066400000000000000000000004641436430751200174760ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 diverse exceptions """ class YamlException(Exception): """exception in CfgYaml""" class UndefinedException(Exception): """exception in templating""" class UnmetDependency(Exception): """unmet dependency""" dotdrop-1.12.9/dotdrop/importer.py000066400000000000000000000244231436430751200171570ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2020, deadc0de6 handle import of dotfiles """ import os import shutil # local imports from dotdrop.logger import Logger from dotdrop.utils import strip_home, get_default_file_perms, \ get_file_perm, get_umask, must_ignore, \ get_unique_tmp_name, removepath from dotdrop.linktypes import LinkTypes from dotdrop.comparator import Comparator from dotdrop.templategen import Templategen from dotdrop.exceptions import UndefinedException class Importer: """dotfile importer""" def __init__(self, profile, conf, dotpath, diff_cmd, variables, dry=False, safe=True, debug=False, keepdot=True, ignore=None): """constructor @profile: the selected profile @conf: configuration manager @dotpath: dotfiles dotpath @diff_cmd: diff command to use @variables: dictionary of variables for the templates @dry: simulate @safe: ask for overwrite if True @debug: enable debug @keepdot: keep dot prefix @ignore: patterns to ignore when importing This may raise UndefinedException """ self.profile = profile if not self.profile: raise UndefinedException("profile is undefined") self.conf = conf self.dotpath = dotpath self.diff_cmd = diff_cmd self.variables = variables self.dry = dry self.safe = safe self.debug = debug self.keepdot = keepdot self.ignore = ignore or [] self.templater = Templategen(variables=self.variables, base=self.dotpath, debug=self.debug) self.umask = get_umask() self.log = Logger(debug=self.debug) def import_path(self, path, import_as=None, import_link=LinkTypes.NOLINK, import_mode=False, import_transw="", import_transr=""): """ import a dotfile pointed by path returns: 1: 1 dotfile imported 0: ignored -1: error """ path = os.path.abspath(path) self.log.dbg(f'import {path}') if not os.path.exists(path): self.log.err(f'\"{path}\" does not exist, ignored!') return -1 # check transw if any trans_write = None trans_read = None if import_transw: trans_write = self.conf.get_trans_w(import_transw) if import_transr: trans_read = self.conf.get_trans_r(import_transr) return self._import(path, import_as=import_as, import_link=import_link, import_mode=import_mode, trans_write=trans_write, trans_read=trans_read) def _import(self, path, import_as=None, import_link=LinkTypes.NOLINK, import_mode=False, trans_write=None, trans_read=None): """ import path returns: 1: 1 dotfile imported 0: ignored -1: error """ # normalize path dst = path.rstrip(os.sep) dst = os.path.abspath(dst) # test if must be ignored if self._ignore(dst): return 0 # ask confirmation for symlinks if self.safe: realdst = os.path.realpath(dst) if dst != realdst: msg = f'\"{dst}\" is a symlink, dereference it and continue?' if not self.log.ask(msg): return 0 # create src path src = strip_home(dst) if import_as: # handle import as src = os.path.expanduser(import_as) src = src.rstrip(os.sep) src = os.path.abspath(src) src = strip_home(src) self.log.dbg(f'import src for {dst} as {src}') # with or without dot prefix strip = '.' + os.sep if self.keepdot: strip = os.sep src = src.lstrip(strip) # get the permission perm = get_file_perm(dst) # get the link attribute linktype = import_link if linktype == LinkTypes.LINK_CHILDREN and \ not os.path.isdir(path): self.log.err(f'importing \"{path}\" failed!') return -1 if self._already_exists(src, dst): return -1 self.log.dbg(f'import dotfile: src:{src} dst:{dst}') if not self._import_file(src, dst, trans_write=trans_write): return -1 return self._import_in_config(path, src, dst, perm, linktype, import_mode, trans_w=trans_write, trans_r=trans_read) def _import_in_config(self, path, src, dst, perm, linktype, import_mode, trans_r=None, trans_w=None): """ import path returns: 1: 1 dotfile imported 0: ignored """ # handle file mode chmod = None dflperm = get_default_file_perms(dst, self.umask) self.log.dbg(f'import chmod: {import_mode}') if import_mode or perm != dflperm: msg = f'adopt mode {perm:o} (umask {dflperm:o})' self.log.dbg(msg) chmod = perm # add file to config file retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod, trans_read=trans_r, trans_write=trans_w) if not retconf: self.log.warn(f'\"{path}\" ignored during import') return 0 self.log.sub(f'\"{path}\" imported') return 1 def _check_existing_dotfile(self, src, dst): """ check if a dotfile in the dotpath already exists for this src """ if not os.path.exists(src): return True if not self.safe: return True cmp = Comparator(debug=self.debug, diff_cmd=self.diff_cmd) diff = cmp.compare(src, dst) if diff != '': # files are different, dunno what to do self.log.log(f'diff \"{dst}\" VS \"{src}\"') self.log.emph(diff) # ask user msg = f'Dotfile \"{src}\" already exists, overwrite?' if not self.log.ask(msg): return False self.log.dbg('will overwrite existing file') return True def _import_file(self, src, dst, trans_write=None): """ prepare hierarchy for dotfile in dotpath and copy file src is file in dotpath dst is file on filesystem """ srcf = os.path.join(self.dotpath, src) srcfd = os.path.dirname(srcf) # check if must be ignored if self._ignore(srcf) or self._ignore(srcfd): return False # check we are not overwritting if not self._check_existing_dotfile(srcf, dst): return False # create directory hierarchy if self.dry: cmd = f'mkdir -p {srcfd}' self.log.dry(f'would run: {cmd}') else: try: os.makedirs(srcfd, exist_ok=True) except OSError: self.log.err(f'importing \"{dst}\" failed!') return False # import the file if self.dry: self.log.dry(f'would copy {dst} to {srcf}') else: # apply trans_w dst = self._apply_trans_w(dst, trans_write) if not dst: # transformation failed return False # copy the file to the dotpath try: if os.path.isdir(dst): if os.path.exists(srcf): shutil.rmtree(srcf) ign = shutil.ignore_patterns(*self.ignore) shutil.copytree(dst, srcf, copy_function=self._cp, ignore=ign) else: shutil.copy2(dst, srcf) except shutil.Error as exc: src = exc.args[0][0][0] why = exc.args[0][0][2] self.log.err(f'importing \"{src}\" failed: {why}') return True def _cp(self, src, dst): """the copy function for copytree""" # test if must be ignored if self._ignore(src): return shutil.copy2(src, dst) def _already_exists(self, src, dst): """ test no other dotfile exists with same dst for this profile but different src """ dfs = self.conf.get_dotfile_by_dst(dst, profile_key=self.profile) if not dfs: return False for dotfile in dfs: profiles = self.conf.get_profiles_by_dotfile_key(dotfile.key) profiles = [x.key for x in profiles] if self.profile in profiles and \ not self.conf.get_dotfile_by_src_dst(src, dst): # same profile # different src msg = f'duplicate dotfile: {dotfile.key}' self.log.err(msg) return True return False def _ignore(self, path): if must_ignore([path], self.ignore, debug=self.debug): self.log.dbg(f'ignoring import of {path}') self.log.warn(f'{path} ignored') return True return False def _apply_trans_w(self, path, trans): """ apply transformation to path on filesystem) returns - the new path (tmp file) if trans - original path if no trans - None/empty string if error """ if not trans: return path self.log.dbg(f'executing write transformation {trans}') tmp = get_unique_tmp_name() if not trans.transform(path, tmp, debug=self.debug, templater=self.templater): msg = f'transformation \"{trans.key}\" failed for {path}' self.log.err(msg) if os.path.exists(tmp): removepath(tmp, logger=self.log) return None return tmp dotdrop-1.12.9/dotdrop/installer.py000066400000000000000000000722341436430751200173160ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 handle the installation of dotfiles """ import os import errno import shutil # local imports from dotdrop.logger import Logger from dotdrop.linktypes import LinkTypes from dotdrop import utils from dotdrop.exceptions import UndefinedException from dotdrop.cfg_yaml import CfgYaml class Installer: """dotfile installer""" def __init__(self, base='.', create=True, backup=True, dry=False, safe=False, workdir='~/.config/dotdrop', debug=False, diff=True, totemp=None, showdiff=False, backup_suffix='.dotdropbak', diff_cmd=''): """ @base: directory path where to search for templates @create: create directory hierarchy if missing when installing @backup: backup existing dotfile when installing @dry: just simulate @safe: ask for any overwrite @workdir: where to install template before symlinking @debug: enable debug @diff: diff when installing if True @totemp: deploy to this path instead of dotfile dst if not None @showdiff: show the diff before overwriting (or asking for) @backup_suffix: suffix for dotfile backup file @diff_cmd: diff command to use """ self.create = create self.backup = backup self.dry = dry self.safe = safe workdir = os.path.expanduser(workdir) workdir = os.path.normpath(workdir) self.workdir = workdir base = os.path.expanduser(base) base = os.path.normpath(base) self.base = base self.debug = debug self.diff = diff self.totemp = totemp self.showdiff = showdiff self.backup_suffix = backup_suffix self.diff_cmd = diff_cmd self.action_executed = False # avoids printing file copied logs # when using install_to_tmp for comparing self.comparing = False self.log = Logger(debug=self.debug) ######################################################## # public methods ######################################################## def install(self, templater, src, dst, linktype, actionexec=None, noempty=False, ignore=None, is_template=True, chmod=None, force_chmod=False): """ install src to dst @templater: the templater object @src: dotfile source path in dotpath @dst: dotfile destination path in the FS @linktype: linktypes.LinkTypes @actionexec: action executor callback @noempty: render empty template flag @ignore: pattern to ignore when installing @is_template: this dotfile is a template @chmod: rights to apply if any @force_chmod: do not ask user to chmod return - True, None : success - False, error_msg : error - False, None : ignored """ if not src or not dst: # fake dotfile self.log.dbg('fake dotfile installed') self._exec_pre_actions(actionexec) return True, None msg = f'installing \"{src}\" to \"{dst}\" (link: {linktype})' self.log.dbg(msg) src, dst, cont, err = self._check_paths(src, dst) if not cont: return self._log_install(cont, err) # check source file exists src = os.path.join(self.base, src) if not os.path.exists(src): err = f'source dotfile does not exist: {src}' return self._log_install(False, err) self.action_executed = False # install to temporary dir # and ignore any actions if self.totemp: ret, err, _ = self.install_to_temp(templater, self.totemp, src, dst, is_template=is_template, chmod=chmod, ignore=ignore) return self._log_install(ret, err) isdir = os.path.isdir(src) self.log.dbg(f'install {src} to {dst}') self.log.dbg(f'\"{src}\" is a directory: {isdir}') if linktype == LinkTypes.NOLINK: # normal file if isdir: ret, err = self._copy_dir(templater, src, dst, actionexec=actionexec, noempty=noempty, ignore=ignore, is_template=is_template) else: ret, err = self._copy_file(templater, src, dst, actionexec=actionexec, noempty=noempty, ignore=ignore, is_template=is_template) elif linktype in (LinkTypes.LINK, LinkTypes.ABSOLUTE): # symlink ret, err = self._link_absolute(templater, src, dst, actionexec=actionexec, is_template=is_template, ignore=ignore, chmod=chmod) elif linktype == LinkTypes.RELATIVE: # symlink ret, err = self._link_relative(templater, src, dst, actionexec=actionexec, is_template=is_template, ignore=ignore, chmod=chmod) elif linktype == LinkTypes.LINK_CHILDREN: # symlink direct children if not isdir: msg = f'symlink children of {src} to {dst}' self.log.dbg(msg) err = f'source dotfile is not a directory: {src}' ret = False else: ret, err = self._link_children(templater, src, dst, actionexec=actionexec, is_template=is_template, ignore=ignore) if self.log.debug and chmod: cur = utils.get_file_perm(dst) if chmod == CfgYaml.chmod_ignore: chmodstr = CfgYaml.chmod_ignore else: chmodstr = f'{chmod:o}' self.log.dbg( f'before chmod (cur:{cur:o}, new:{chmodstr}): ' f'installed:{ret} err:{err}' ) if self.dry: return self._log_install(ret, err) # handle chmod # - on success (r, not err) # - no change (not r, not err) # but not when # - error (not r, err) # - aborted (not r, err) # - special keyword "preserve" apply_chmod = linktype in [LinkTypes.NOLINK, LinkTypes.LINK_CHILDREN] apply_chmod = apply_chmod and os.path.exists(dst) apply_chmod = apply_chmod and (ret or (not ret and not err)) apply_chmod = apply_chmod and chmod != CfgYaml.chmod_ignore if apply_chmod: if not chmod: chmod = utils.get_file_perm(src) self.log.dbg(f'applying chmod {chmod:o} to {dst}') dstperms = utils.get_file_perm(dst) if dstperms != chmod: # apply mode msg = f'chmod {dst} to {chmod:o}' if not force_chmod and self.safe and not self.log.ask(msg): ret = False err = 'aborted' else: if not self.comparing: self.log.sub(f'chmod {dst} to {chmod:o}') if utils.chmod(dst, chmod, debug=self.debug): ret = True else: ret = False err = 'chmod failed' else: self.log.dbg('no chmod applied') return self._log_install(ret, err) def install_to_temp(self, templater, tmpdir, src, dst, is_template=True, chmod=None, ignore=None, set_create=False): """ install a dotfile to a tempdir @templater: the templater object @tmpdir: where to install @src: dotfile source path in dotpath @dst: dotfile destination path in the FS @is_template: this dotfile is a template @chmod: rights to apply if any @ignore: patterns to ignore @set_create: force create to True return - success, error-if-any, dotfile-installed-path """ self.log.dbg(f'tmp install {src} (defined dst: {dst})') src, dst, cont, err = self._check_paths(src, dst) if not cont: self._log_install(cont, err) return cont, err, None ret = False tmpdst = '' # save flags self.comparing = True drysaved = self.dry self.dry = False diffsaved = self.diff self.diff = False if set_create: createsaved = self.create self.create = True totemp = self.totemp self.totemp = None # install the dotfile to a temp directory tmpdst = utils.pivot_path(dst, tmpdir, logger=self.log) ret, err = self.install(templater, src, tmpdst, LinkTypes.NOLINK, is_template=is_template, chmod=chmod, ignore=ignore) if ret: self.log.dbg(f'tmp installed in {tmpdst}') # restore flags self.dry = drysaved self.diff = diffsaved if set_create: self.create = createsaved self.comparing = False self.totemp = totemp return ret, err, tmpdst ######################################################## # low level accessors for public methods ######################################################## def _link_absolute(self, templater, src, dst, actionexec=None, is_template=True, ignore=None, chmod=None): """ install link:absolute|link return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ return self._link_dotfile(templater, src, dst, actionexec=actionexec, is_template=is_template, ignore=ignore, absolute=True, chmod=chmod) def _link_relative(self, templater, src, dst, actionexec=None, is_template=True, ignore=None, chmod=None): """ install link:relative return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ return self._link_dotfile(templater, src, dst, actionexec=actionexec, is_template=is_template, ignore=ignore, absolute=False, chmod=chmod) def _link_dotfile(self, templater, src, dst, actionexec=None, is_template=True, ignore=None, absolute=True, chmod=None): """ symlink chmod is only used if the dotfile is a template and needs to be installed to the workdir first return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ if is_template: self.log.dbg(f'is a template, installing to {self.workdir}') tmp = utils.pivot_path(dst, self.workdir, striphome=True, logger=self.log) ret, err = self.install(templater, src, tmp, LinkTypes.NOLINK, actionexec=actionexec, is_template=is_template, ignore=ignore, chmod=chmod) if not ret and not os.path.exists(tmp): return ret, err src = tmp ret, err = self._symlink(src, dst, actionexec=actionexec, absolute=absolute) return ret, err def _link_children(self, templater, src, dst, actionexec=None, is_template=True, ignore=None): """ install link:link_children return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ parent = os.path.join(self.base, src) if not os.path.lexists(dst): if self.dry: self.log.dry(f'would create directory "{dst}"') else: if not self.comparing: self.log.sub(f'creating directory "{dst}"') self._create_dirs(dst) if os.path.isfile(dst): msg = ''.join([ f'Remove regular file {dst} and ', 'replace with empty directory?', ]) if self.safe and not self.log.ask(msg): return False, 'aborted' os.unlink(dst) self._create_dirs(dst) children = os.listdir(parent) srcs = [os.path.normpath(os.path.join(parent, child)) for child in children] dsts = [os.path.normpath(os.path.join(dst, child)) for child in children] installed = 0 for i in range(len(children)): subsrc = srcs[i] subdst = dsts[i] if utils.must_ignore([subsrc, subdst], ignore, debug=self.debug): self.log.dbg( f'ignoring install of {src} to {dst}', ) continue self.log.dbg(f'symlink child {subsrc} to {subdst}') if is_template: self.log.dbg('child is a template') self.log.dbg(f'install to {self.workdir} and symlink') tmp = utils.pivot_path(subdst, self.workdir, striphome=True, logger=self.log) ret2, err2 = self.install(templater, subsrc, tmp, LinkTypes.NOLINK, actionexec=actionexec, is_template=is_template, ignore=ignore) if not ret2 and err2 and not os.path.exists(tmp): continue subsrc = tmp ret, err = self._symlink(subsrc, subdst, actionexec=actionexec) if ret: installed += 1 # void actionexec if dotfile installed # to prevent from running actions multiple times actionexec = None else: if err: return ret, err return installed > 0, None ######################################################## # file operations ######################################################## def _symlink(self, src, dst, actionexec=None, absolute=True): """ set src as a link target of dst return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ overwrite = not self.safe if os.path.lexists(dst): # symlink exists if os.path.realpath(dst) == os.path.realpath(src): msg = f'ignoring "{dst}", link already exists' self.log.dbg(msg) return False, None if self.dry: self.log.dry(f'would remove {dst} and link to {src}') return True, None if self.showdiff: self._show_diff_before_write(src, dst) msg = f'Remove "{dst}" for link creation?' if self.safe and not self.log.ask(msg): return False, 'aborted' # remove symlink overwrite = True try: utils.removepath(dst) except OSError as exc: err = f'something went wrong with {src}: {exc}' return False, err if self.dry: self.log.dry(f'would link {dst} to {src}') return True, None base = os.path.dirname(dst) if not self._create_dirs(base): err = f'error creating directory for {dst}' return False, err # execute pre-actions ret, err = self._exec_pre_actions(actionexec) if not ret: return False, err # re-check in case action created the file if os.path.lexists(dst): msg = f'Remove "{dst}" for link creation?' if self.safe and not overwrite and not self.log.ask(msg): return False, 'aborted' try: utils.removepath(dst) except OSError as exc: err = f'something went wrong with {src}: {exc}' return False, err # create symlink lnk_src = src if not absolute: # relative symlink dstrel = dst if not os.path.isdir(dstrel): dstrel = os.path.dirname(dstrel) lnk_src = os.path.relpath(src, dstrel) os.symlink(lnk_src, dst) self.log.dbg( f'symlink {dst} to {lnk_src} ' f'(mode:{utils.get_file_perm(dst):o})' ) if not self.comparing: self.log.sub(f'linked {dst} to {lnk_src}') return True, None def _copy_file(self, templater, src, dst, actionexec=None, noempty=False, ignore=None, is_template=True): """ install src to dst when is a file return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ self.log.dbg(f'deploy file: {src}') self.log.dbg(f'ignore empty: {noempty}') self.log.dbg(f'ignore pattern: {ignore}') self.log.dbg(f'is_template: {is_template}') self.log.dbg(f'no empty: {noempty}') # ignore file if utils.must_ignore([src, dst], ignore, debug=self.debug): self.log.dbg(f'ignoring install of {src} to {dst}') return False, None # check no loop if utils.samefile(src, dst): err = f'dotfile points to itself: {dst}' return False, err # check source file exists if not os.path.exists(src): err = f'source dotfile does not exist: {src}' return False, err # handle the file content = None if is_template: # template the file saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst)) try: content = templater.generate(src) except UndefinedException as exc: return False, str(exc) finally: templater.restore_vars(saved) # test is empty if noempty and utils.content_empty(content): self.log.dbg(f'ignoring empty template: {src}') return False, None if content is None: err = f'empty template {src}' return False, err # write the file ret, err = self._write(src, dst, content=content, actionexec=actionexec) if ret and not err: rights = f'{utils.get_file_perm(src):o}' self.log.dbg(f'installed file {src} to {dst} ({rights})') if not self.dry and not self.comparing: self.log.sub(f'install {src} to {dst}') return ret, err def _copy_dir(self, templater, src, dst, actionexec=None, noempty=False, ignore=None, is_template=True): """ install src to dst when is a directory return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ self.log.dbg(f'deploy dir {src}') # default to nothing installed and no error ret = False, None # handle all files in dir for entry in os.listdir(src): fpath = os.path.join(src, entry) self.log.dbg(f'deploy sub from {dst}: {entry}') if not os.path.isdir(fpath): # is file res, err = self._copy_file(templater, fpath, os.path.join(dst, entry), actionexec=actionexec, noempty=noempty, ignore=ignore, is_template=is_template) if not res and err: # error occured return res, err if res: # something got installed ret = True, None else: # is directory res, err = self._copy_dir(templater, fpath, os.path.join(dst, entry), actionexec=actionexec, noempty=noempty, ignore=ignore, is_template=is_template) if not res and err: # error occured return res, err if res: # something got installed ret = True, None return ret @classmethod def _write_content_to_file(cls, content, src, dst): """write content to file""" if content: # write content the file try: with open(dst, 'wb') as file: file.write(content) # shutil.copymode(src, dst) except NotADirectoryError as exc: err = f'opening dest file: {exc}' return False, err except OSError as exc: return False, str(exc) except TypeError as exc: return False, str(exc) else: # copy file try: shutil.copyfile(src, dst) # shutil.copymode(src, dst) except OSError as exc: return False, str(exc) return True, None def _write(self, src, dst, content=None, actionexec=None): """ copy dotfile / write content to file return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ overwrite = not self.safe if self.dry: self.log.dry(f'would install {dst}') return True, None if os.path.lexists(dst): # file/symlink exists try: os.stat(dst) except OSError as exc: if exc.errno == errno.ENOENT: # broken symlink err = f'broken symlink {dst}' return False, err if self.diff: if not self._is_different(src, dst, content=content): self.log.dbg(f'{dst} is the same') return False, None if self.safe: self.log.dbg(f'change detected for {dst}') if self.showdiff: # get diff self._show_diff_before_write(src, dst, content=content) if not self.log.ask(f'Overwrite \"{dst}\"'): return False, 'aborted' overwrite = True if self.backup: self._backup(dst) # create hierarchy base = os.path.dirname(dst) if not self._create_dirs(base): err = f'creating directory for {dst}' return False, err # execute pre actions ret, err = self._exec_pre_actions(actionexec) if not ret: return False, err self.log.dbg(f'installing file to \"{dst}\"') # re-check in case action created the file if self.safe and not overwrite and \ os.path.lexists(dst) and \ not self.log.ask(f'Overwrite \"{dst}\"'): self.log.warn(f'ignoring {dst}') return False, 'aborted' # writing to file self.log.dbg(f'before writing to {dst} ({utils.get_file_perm(src):o})') ret = self._write_content_to_file(content, src, dst) self.log.dbg(f'written to {dst} ({utils.get_file_perm(src):o})') return ret ######################################################## # helpers ######################################################## @classmethod def _get_tmp_file_vars(cls, src, dst): tmp = {} tmp['_dotfile_sub_abs_src'] = src tmp['_dotfile_sub_abs_dst'] = dst return tmp def _is_different(self, src, dst, content=None): """ returns True if file is different and needs to be installed """ # check file content tmp = None if content: tmp = utils.write_to_tmpfile(content) src = tmp ret = utils.fastdiff(src, dst) if ret: self.log.dbg('content differ') if content: utils.removepath(tmp) return ret def _show_diff_before_write(self, src, dst, content=None): """ diff before writing using a temp file if content is not None returns diff string ('' if same) """ tmp = None if content: tmp = utils.write_to_tmpfile(content) src = tmp diff = utils.diff(modified=src, original=dst, diff_cmd=self.diff_cmd) if tmp: utils.removepath(tmp, logger=self.log) if diff: self._print_diff(src, dst, diff) return diff def _print_diff(self, src, dst, diff): """show diff to user""" self.log.log(f'diff \"{dst}\" VS \"{src}\"') self.log.emph(diff) def _create_dirs(self, directory): """mkdir -p """ if not self.create and not os.path.exists(directory): self.log.dbg('no mkdir as \"create\" set to false in config') return False if os.path.exists(directory): return True if self.dry: self.log.dry(f'would mkdir -p {directory}') return True self.log.dbg(f'mkdir -p {directory}') os.makedirs(directory, exist_ok=True) return os.path.exists(directory) def _backup(self, path): """backup file pointed by path""" if self.dry: return dst = path.rstrip(os.sep) + self.backup_suffix self.log.log(f'backup {path} to {dst}') # os.rename(path, dst) # copy to preserve mode on chmod=preserve # since we expect dotfiles this shouldn't have # such a big impact but who knows. shutil.copy2(path, dst) stat = os.stat(path) os.chown(dst, stat.st_uid, stat.st_gid) def _exec_pre_actions(self, actionexec): """execute action executor""" if self.action_executed: return True, None if not actionexec: return True, None ret, err = actionexec() self.action_executed = True return ret, err def _log_install(self, boolean, err): """ log installation process returns success, error-if-any """ if not self.debug: return boolean, err if boolean: self.log.dbg('install: SUCCESS') else: if err: self.log.dbg(f'install: ERROR: {err}') else: self.log.dbg('install: IGNORED') return boolean, err def _check_paths(self, src, dst): """ check and normalize param returns , , , """ # check both path are valid if not dst or not src: err = f'empty dst or src for {src}' self.log.dbg(err) return None, None, False, err # normalize src and dst src = os.path.expanduser(src) src = os.path.normpath(src) dst = os.path.expanduser(dst) dst = os.path.normpath(dst) return src, dst, True, None dotdrop-1.12.9/dotdrop/jhelpers.py000066400000000000000000000010711436430751200171240ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2018, deadc0de6 jinja2 helper methods """ import os import shutil def exists(path): """return true when path exists""" return os.path.exists(os.path.expandvars(path)) def exists_in_path(name, path=None): """return true when executable exists in os path""" return shutil.which(name, os.F_OK | os.X_OK, path) is not None def basename(path): """return basename""" return os.path.basename(path) def dirname(path): """return dirname""" return os.path.dirname(path) dotdrop-1.12.9/dotdrop/linktypes.py000066400000000000000000000014421436430751200173340ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2020, deadc0de6 represents a type of link in dotdrop """ # https://github.com/PyCQA/pylint/issues/2062 # pylint: disable=E1101 from enum import IntEnum class LinkTypes(IntEnum): """a type of link""" NOLINK = 0 LINK = 1 LINK_CHILDREN = 2 ABSOLUTE = 3 RELATIVE = 4 @classmethod def get(cls, key, default=None): """get the linktype""" try: return key if isinstance(key, cls) else cls[key.upper()] except KeyError as exc: if default: return default err = f'bad {cls.__name__} value: "{key}"' raise ValueError(err) from exc def __str__(self): """linktype to string""" return self.name.lower() dotdrop-1.12.9/dotdrop/logger.py000066400000000000000000000060731436430751200165760ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 provide logging functions """ import sys import inspect class Logger: """logging facility for dotdrop""" RED = '\033[91m' GREEN = '\033[92m' YELLOW = '\033[93m' BLUE = '\033[94m' MAGENTA = '\033[95m' LMAGENTA = '\033[35m' RESET = '\033[0m' EMPH = '\033[33m' BOLD = '\033[1m' def __init__(self, debug=False): self.debug = debug def log(self, string, end='\n', pre='', bold=False): """normal log""" cstart = self._color(self.BLUE) cend = self._color(self.RESET) if bold: bold = self._color(self.BOLD) fmt = f'{pre}{cstart}{bold}{string}{cend}' fmt += f'{end}{cend}' else: fmt = f'{pre}{cstart}{string}{end}{cend}' sys.stdout.write(fmt) def sub(self, string, end='\n'): """sub log""" cstart = self._color(self.BLUE) cend = self._color(self.RESET) sys.stdout.write(f'\t{cstart}->{cend} {string}{end}') def emph(self, string, stdout=True): """emphasis log""" cstart = self._color(self.EMPH) cend = self._color(self.RESET) content = f'{cstart}{string}{cend}' if not stdout: sys.stderr.write(content) else: sys.stdout.write(content) def err(self, string, end='\n'): """error log""" cstart = self._color(self.RED) cend = self._color(self.RESET) msg = f'{string} {end}' sys.stderr.write(f'{cstart}[ERR] {msg}{cend}') def warn(self, string, end='\n'): """warning log""" cstart = self._color(self.YELLOW) cend = self._color(self.RESET) sys.stderr.write(f'{cstart}[WARN] {string} {end}{cend}') def dbg(self, string, force=False): """debug log""" if not force and not self.debug: return frame = inspect.stack()[1] mod = inspect.getmodule(frame[0]).__name__ func = inspect.stack()[1][3] cstart = self._color(self.MAGENTA) cend = self._color(self.RESET) clight = self._color(self.LMAGENTA) bold = self._color(self.BOLD) line = f'{bold}{clight}[DEBUG][{mod}.{func}]' line += f'{cend}{cstart} {string}{cend}\n' sys.stderr.write(line) def dry(self, string, end='\n'): """dry run log""" cstart = self._color(self.GREEN) cend = self._color(self.RESET) sys.stdout.write(f'{cstart}[DRY] {string} {end}{cend}') @classmethod def raw(cls, string, end='\n'): """raw log""" sys.stdout.write(f'{string}{end}') def ask(self, query): """ask user for confirmation""" cstart = self._color(self.BLUE) cend = self._color(self.RESET) question = query + ' [y/N] ? ' qmsg = f'{cstart}{question}{cend}' resp = input(qmsg) return resp == 'y' @classmethod def _color(cls, col): """is color supported""" if not sys.stdout.isatty(): return '' return col dotdrop-1.12.9/dotdrop/options.py000066400000000000000000000351601436430751200170110ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 stores all options to use across dotdrop """ # attribute-defined-outside-init # pylint: disable=W0201 import os import sys import socket from docopt import docopt # local imports from dotdrop.version import __version__ as VERSION from dotdrop.linktypes import LinkTypes from dotdrop.logger import Logger from dotdrop.cfg_aggregator import CfgAggregator from dotdrop.action import Action from dotdrop.utils import uniq_list, debug_list, debug_dict from dotdrop.exceptions import YamlException ENV_PROFILE = 'DOTDROP_PROFILE' ENV_CONFIG = 'DOTDROP_CONFIG' ENV_NOBANNER = 'DOTDROP_NOBANNER' ENV_DEBUG = 'DOTDROP_DEBUG' ENV_NODEBUG = 'DOTDROP_FORCE_NODEBUG' ENV_XDG = 'XDG_CONFIG_HOME' ENV_WORKERS = 'DOTDROP_WORKERS' BACKUP_SUFFIX = '.dotdropbak' PROFILE = socket.gethostname() if ENV_PROFILE in os.environ: PROFILE = os.environ[ENV_PROFILE] NAME = 'dotdrop' CONFIGFILEYAML = 'config.yaml' CONFIGFILETOML = 'config.toml' HOMECFG = f'~/.config/{NAME}' ETCXDGCFG = f'/etc/xdg/{NAME}' ETCCFG = f'/etc/{NAME}' OPT_LINK = { LinkTypes.NOLINK.name.lower(): LinkTypes.NOLINK, LinkTypes.ABSOLUTE.name.lower(): LinkTypes.ABSOLUTE, LinkTypes.RELATIVE.name.lower(): LinkTypes.RELATIVE, LinkTypes.LINK_CHILDREN.name.lower(): LinkTypes.LINK_CHILDREN} BANNER = fr""" _ _ _ __| | ___ | |_ __| |_ __ ___ _ __ / _` |/ _ \| __/ _` | '__/ _ \| '_ | \__,_|\___/ \__\__,_|_| \___/| .__/ v{VERSION} |_|""" USAGE = f""" {BANNER} Usage: dotdrop install [-VbtfndDaW] [-c ] [-p ] [-w ] [...] dotdrop import [-Vbdfm] [-c ] [-p ] [-i ...] [--transr=] [--transw=] [-l ] [-s ] ... dotdrop compare [-LVbz] [-c ] [-p ] [-w ] [-C ...] [-i ...] dotdrop update [-VbfdkPz] [-c ] [-p ] [-w ] [-i ...] [...] dotdrop remove [-Vbfdk] [-c ] [-p ] [...] dotdrop files [-VbTG] [-c ] [-p ] dotdrop detail [-Vb] [-c ] [-p ] [...] dotdrop profiles [-VbG] [-c ] dotdrop --help dotdrop --version Options: -a --force-actions Execute all actions even if no dotfile is installed. -b --no-banner Do not display the banner. -c --cfg= Path to the config. -C --file= Path of dotfile to compare. -d --dry Dry run. -D --showdiff Show a diff before overwriting. -f --force Do not ask user confirmation for anything. -G --grepable Grepable output. -i --ignore= Pattern to ignore. -k --key Treat as a dotfile key. -l --link= Link option (nolink|absolute|relative|link_children). -L --file-only Do not show diff but only the files that differ. -m --preserve-mode Insert a chmod entry in the dotfile with its mode. -n --nodiff Do not diff when installing. -p --profile= Specify the profile to use [default: {PROFILE}]. -P --show-patch Provide a one-liner to manually patch template. -s --as= Import as a different path from actual path. --transr= Associate trans_read key on import. --transw= Apply trans_write key on import. -t --temp Install to a temporary directory for review. -T --template Only template dotfiles. -V --verbose Be verbose. -w --workers= Number of concurrent workers [default: 1]. -W --workdir-clear Clear the workdir. -z --ignore-missing Ignore files in installed folders that are missing. -v --version Show version. -h --help Show this screen. """ class AttrMonitor: """monitor attribute setter""" _set_attr_err = False # pylint: disable=W0235 def __setattr__(self, key, value): """monitor attribute setting""" super().__setattr__(key, value) # pylint: enable=W0235 def _attr_set(self, attr): """do something when unexistent attr is set""" class Options(AttrMonitor): """dotdrop options manager""" def __init__(self, args=None): """constructor @args: argument dictionary (if None use sys) """ # attributes gotten from self.conf.get_settings() self.banner = None self.showdiff = None self.default_actions = [] self.instignore = None self.force_chmod = None self.cmpignore = None self.impignore = None self.upignore = None self.link_on_import = None self.chmod_on_import = None self.check_version = None self.clear_workdir = None self.key_prefix = None self.key_separator = None # args parsing self.args = {} if not args: self.args = docopt(USAGE, version=VERSION) if args: self.args = args.copy() self.debug = self.args['--verbose'] or ENV_DEBUG in os.environ self.log = Logger(debug=self.debug) self.dry = self.args['--dry'] if ENV_NODEBUG in os.environ: # force disabling debugs self.debug = False # selected profile self.profile = self.args['--profile'] self.confpath = self._get_config_path() if not self.confpath: raise YamlException('no config file found') if not os.path.exists(self.confpath): err = f'bad config file path: {self.confpath}' raise YamlException(err) self.log.dbg('#################################################') self.log.dbg('#################### DOTDROP ####################') self.log.dbg('#################################################') self.log.dbg(f'version: {VERSION}') args = ' '.join(sys.argv) self.log.dbg(f'command: {args}') self.log.dbg(f'config file: {self.confpath}') self._read_config() self._apply_args() self._fill_attr() if ENV_NOBANNER not in os.environ \ and self.banner \ and not self.args['--no-banner']: self._header() self._debug_attr() # start monitoring for bad attribute self._set_attr_err = True @classmethod def _get_config_from_env(cls, name): # look in XDG_CONFIG_HOME if ENV_XDG in os.environ: cfg = os.path.expanduser(os.environ[ENV_XDG]) path = os.path.join(cfg, NAME, name) if os.path.exists(path): return path return '' @classmethod def _get_config_from_fs(cls, name): """get config from filesystem""" # look in ~/.config/dotdrop cfg = os.path.expanduser(HOMECFG) path = os.path.join(cfg, name) if os.path.exists(path): return path # look in /etc/xdg/dotdrop path = os.path.join(ETCXDGCFG, name) if os.path.exists(path): return path # look in /etc/dotdrop path = os.path.join(ETCCFG, name) if os.path.exists(path): return path return '' def _get_config_path(self): """get the config path""" # cli provided if self.args['--cfg']: return os.path.expanduser(self.args['--cfg']) # environment variable provided if ENV_CONFIG in os.environ: return os.path.expanduser(os.environ[ENV_CONFIG]) # look in current directory if os.path.exists(CONFIGFILEYAML): return CONFIGFILEYAML # look in current directory if os.path.exists(CONFIGFILETOML): return CONFIGFILETOML path = self._get_config_from_env(CONFIGFILEYAML) if path: return path path = self._get_config_from_env(CONFIGFILETOML) if path: return path path = self._get_config_from_fs(CONFIGFILEYAML) if path: return path path = self._get_config_from_fs(CONFIGFILETOML) if path: return path return None def _header(self): """display the header""" self.log.log(BANNER) self.log.log('') def _read_config(self): """read the config file""" self.conf = CfgAggregator(self.confpath, self.profile, debug=self.debug, dry=self.dry) # transform the config settings to self attribute settings = self.conf.get_settings() debug_dict('effective settings', settings, self.debug) for k, val in settings.items(): setattr(self, k, val) def _apply_args_files(self): """files specifics""" self.files_templateonly = self.args['--template'] self.files_grepable = self.args['--grepable'] def _apply_args_install(self): """install specifics""" self.install_force_action = self.args['--force-actions'] self.install_temporary = self.args['--temp'] self.install_keys = self.args[''] self.install_diff = not self.args['--nodiff'] self.install_showdiff = self.showdiff or self.args['--showdiff'] self.install_backup_suffix = BACKUP_SUFFIX self.install_default_actions_pre = [a for a in self.default_actions if a.kind == Action.pre] self.install_default_actions_post = [a for a in self.default_actions if a.kind == Action.post] self.install_ignore = self.instignore self.install_force_chmod = self.force_chmod self.install_clear_workdir = self.args['--workdir-clear'] or \ self.clear_workdir def _apply_args_compare(self): """compare specifics""" self.compare_focus = self.args['--file'] self.compare_ignore = self.args['--ignore'] self.compare_ignore.extend(self.cmpignore) self.compare_ignore.append(f'*{self.install_backup_suffix}') self.compare_ignore = uniq_list(self.compare_ignore) self.compare_fileonly = self.args['--file-only'] self.ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \ self.args['--ignore-missing'] def _apply_args_import(self): """import specifics""" self.import_path = self.args[''] self.import_as = self.args['--as'] self.import_mode = self.args['--preserve-mode'] or self.chmod_on_import self.import_ignore = self.args['--ignore'] self.import_ignore.extend(self.impignore) self.import_ignore.append(f'*{self.install_backup_suffix}') self.import_ignore = uniq_list(self.import_ignore) self.import_transw = self.args['--transw'] self.import_transr = self.args['--transr'] def _apply_args_update(self): """update specifics""" self.update_path = self.args[''] self.update_iskey = self.args['--key'] self.update_ignore = self.args['--ignore'] self.update_ignore.extend(self.upignore) self.update_ignore.append(f'*{self.install_backup_suffix}') self.update_ignore = uniq_list(self.update_ignore) self.update_showpatch = self.args['--show-patch'] def _apply_args_profiles(self): """profiles specifics""" self.profiles_grepable = self.args['--grepable'] def _apply_args_remove(self): """remove specifics""" self.remove_path = self.args[''] self.remove_iskey = self.args['--key'] def _apply_args_detail(self): """detail specifics""" self.detail_keys = self.args[''] def _apply_args(self): """apply cli args as attribute""" # the commands self.cmd_profiles = self.args['profiles'] self.cmd_files = self.args['files'] self.cmd_install = self.args['install'] self.cmd_compare = self.args['compare'] self.cmd_import = self.args['import'] self.cmd_update = self.args['update'] self.cmd_detail = self.args['detail'] self.cmd_remove = self.args['remove'] # adapt attributes based on arguments self.safe = not self.args['--force'] try: if ENV_WORKERS in os.environ: workers = int(os.environ[ENV_WORKERS]) else: workers = int(self.args['--workers']) self.workers = workers except ValueError: self.log.err('bad option for --workers') sys.exit(USAGE) # import link default value self.import_link = self.link_on_import if self.args['--link']: # overwrite default import link with cli switch link = self.args['--link'] if link not in OPT_LINK: self.log.err(f'bad option for --link: {link}') sys.exit(USAGE) self.import_link = OPT_LINK[link] # "files" specifics self._apply_args_files() # "install" specifics self._apply_args_install() # "compare" specifics self._apply_args_compare() # "import" specifics self._apply_args_import() # "update" specifics self._apply_args_update() # "profiles" specifics self._apply_args_profiles() # "detail" specifics self._apply_args_detail() # "remove" specifics self._apply_args_remove() def _fill_attr(self): """create attributes from conf""" # defined variables self.variables = self.conf.get_variables() # dotfiles for this profile self.dotfiles = self.conf.get_dotfiles(profile_key=self.profile) # all defined profiles self.profiles = self.conf.get_profiles() def _debug_attr(self): """debug display all of this class attributes""" if not self.debug: return self.log.dbg('effective options:') for att in dir(self): if att.startswith('_'): continue val = getattr(self, att) if callable(val): continue if isinstance(val, list): debug_list(f'-> {att}', val, self.debug) elif isinstance(val, dict): debug_dict(f'-> {att}', val, self.debug) else: self.log.dbg(f'-> {att}: {val}') def _attr_set(self, attr): """error when some inexistent attr is set""" raise Exception(f'bad option: {attr}') dotdrop-1.12.9/dotdrop/profile.py000066400000000000000000000031321436430751200167500ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 represent a profile in dotdrop """ from dotdrop.dictparser import DictParser from dotdrop.action import Action class Profile(DictParser): """dotdrop profile""" # profile keys key_include = 'include' key_import = 'import' def __init__(self, key, actions=None, dotfiles=None, variables=None, dynvariables=None): """ constructor @key: profile key @actions: list of action keys @dotfiles: list of dotfile keys @variables: list of variable keys @dynvariables: list of interpreted variable keys """ self.key = key self.actions = actions or [] self.dotfiles = dotfiles or [] self.variables = variables or [] self.dynvariables = dynvariables or [] def get_pre_actions(self): """return all 'pre' actions""" return [a for a in self.actions if a.kind == Action.pre] def get_post_actions(self): """return all 'post' actions""" return [a for a in self.actions if a.kind == Action.post] @classmethod def _adjust_yaml_keys(cls, value): """patch dict""" value.pop(cls.key_import, None) value.pop(cls.key_include, None) return value def __eq__(self, other): return self.__dict__ == other.__dict__ def __hash__(self): return (hash(self.key) ^ hash(tuple(self.dotfiles))) def __str__(self): return f'key:"{self.key}"' def __repr__(self): return f'profile({self})' dotdrop-1.12.9/dotdrop/settings.py000066400000000000000000000144071436430751200171570ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 settings block """ import os from dotdrop.exceptions import YamlException # local imports from dotdrop.linktypes import LinkTypes from dotdrop.dictparser import DictParser from dotdrop.utils import is_bin_in_path ENV_WORKDIR = 'DOTDROP_WORKDIR' class Settings(DictParser): """Settings block in config""" # key in yaml file key_yaml = 'config' # settings item keys key_backup = 'backup' key_banner = 'banner' key_create = 'create' key_default_actions = 'default_actions' key_dotpath = 'dotpath' key_ignoreempty = 'ignoreempty' key_keepdot = 'keepdot' key_longkey = 'longkey' key_link_dotfile_default = 'link_dotfile_default' key_link_on_import = 'link_on_import' key_showdiff = 'showdiff' key_upignore = 'upignore' key_impignore = 'impignore' key_cmpignore = 'cmpignore' key_instignore = 'instignore' key_workdir = 'workdir' key_minversion = 'minversion' key_func_file = 'func_file' key_filter_file = 'filter_file' key_diff_command = 'diff_command' key_force_chmod = 'force_chmod' key_template_dotfile_default = 'template_dotfile_default' key_ignore_missing_in_dotdrop = 'ignore_missing_in_dotdrop' key_chmod_on_import = 'chmod_on_import' key_check_version = 'check_version' key_clear_workdir = 'clear_workdir' key_compare_workdir = 'compare_workdir' key_key_prefix = 'key_prefix' key_key_separator = 'key_separator' # import keys key_import_actions = 'import_actions' key_import_configs = 'import_configs' key_import_variables = 'import_variables' # defaults default_diff_cmd = 'diff -r -u {0} {1}' def __init__(self, backup=True, banner=True, create=True, default_actions=None, dotpath='dotfiles', ignoreempty=False, import_actions=None, import_configs=None, import_variables=None, keepdot=False, link_dotfile_default=LinkTypes.NOLINK, link_on_import=LinkTypes.NOLINK, longkey=False, upignore=None, cmpignore=None, instignore=None, impignore=None, workdir='~/.config/dotdrop', showdiff=False, minversion=None, func_file=None, filter_file=None, diff_command=default_diff_cmd, template_dotfile_default=True, ignore_missing_in_dotdrop=False, force_chmod=False, chmod_on_import=False, check_version=False, clear_workdir=False, compare_workdir=False, key_prefix=True, key_separator='_'): self.backup = backup self.banner = banner self.create = create self.default_actions = default_actions or [] self.dotpath = dotpath self.ignoreempty = ignoreempty self.import_actions = import_actions or [] self.import_configs = import_configs or [] self.import_variables = import_variables or [] self.keepdot = keepdot self.longkey = longkey self.showdiff = showdiff self.upignore = upignore or [] self.cmpignore = cmpignore or [] self.instignore = instignore or [] self.impignore = impignore or [] self.workdir = workdir if ENV_WORKDIR in os.environ: self.workdir = os.environ[ENV_WORKDIR] self.link_dotfile_default = LinkTypes.get(link_dotfile_default) self.link_on_import = LinkTypes.get(link_on_import) self.minversion = minversion self.func_file = func_file or [] self.filter_file = filter_file or [] self.diff_command = diff_command self.template_dotfile_default = template_dotfile_default self.ignore_missing_in_dotdrop = ignore_missing_in_dotdrop self.force_chmod = force_chmod self.chmod_on_import = chmod_on_import self.check_version = check_version self.clear_workdir = clear_workdir self.compare_workdir = compare_workdir self.key_prefix = key_prefix self.key_separator = key_separator # check diff command if not is_bin_in_path(self.diff_command): err = f'bad diff_command: {diff_command}' raise YamlException(err) def _serialize_seq(self, name, dic): """serialize attribute 'name' into 'dic'""" seq = getattr(self, name) dic[name] = seq def serialize(self): """Return key-value pair representation of the settings""" dic = { self.key_backup: self.backup, self.key_banner: self.banner, self.key_create: self.create, self.key_dotpath: self.dotpath, self.key_ignoreempty: self.ignoreempty, self.key_keepdot: self.keepdot, self.key_link_dotfile_default: str(self.link_dotfile_default), self.key_link_on_import: str(self.link_on_import), self.key_longkey: self.longkey, self.key_showdiff: self.showdiff, self.key_workdir: self.workdir, self.key_minversion: self.minversion, self.key_diff_command: self.diff_command, self.key_template_dotfile_default: self.template_dotfile_default, self.key_ignore_missing_in_dotdrop: self.ignore_missing_in_dotdrop, self.key_force_chmod: self.force_chmod, self.key_chmod_on_import: self.chmod_on_import, self.key_check_version: self.check_version, self.key_clear_workdir: self.clear_workdir, self.key_compare_workdir: self.compare_workdir, self.key_key_prefix: self.key_prefix, self.key_key_separator: self.key_separator, } self._serialize_seq(self.key_default_actions, dic) self._serialize_seq(self.key_import_actions, dic) self._serialize_seq(self.key_import_configs, dic) self._serialize_seq(self.key_import_variables, dic) self._serialize_seq(self.key_cmpignore, dic) self._serialize_seq(self.key_upignore, dic) self._serialize_seq(self.key_instignore, dic) self._serialize_seq(self.key_impignore, dic) self._serialize_seq(self.key_func_file, dic) self._serialize_seq(self.key_filter_file, dic) return {self.key_yaml: dic} dotdrop-1.12.9/dotdrop/templategen.py000066400000000000000000000232331436430751200176210ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 jinja2 template generator """ import os import io import re import mmap from jinja2 import Environment, FileSystemLoader, \ ChoiceLoader, FunctionLoader, TemplateNotFound, \ StrictUndefined from jinja2.exceptions import UndefinedError # local imports from dotdrop import utils from dotdrop import jhelpers from dotdrop.logger import Logger from dotdrop.exceptions import UndefinedException BLOCK_START = '{%@@' BLOCK_END = '@@%}' VAR_START = '{{@@' VAR_END = '@@}}' COMMENT_START = '{#@@' COMMENT_END = '@@#}' LOG = Logger() class Templategen: """dotfile templater""" def __init__(self, base='.', variables=None, func_file=None, filter_file=None, debug=False): """constructor @base: directory path where to search for templates @variables: dictionary of variables for templates @func_file: file path to load functions from @filter_file: file path to load filters from @debug: enable debug """ self.base = base.rstrip(os.sep) self.debug = debug self.log = Logger(debug=self.debug) self.log.dbg('loading templategen') self.variables = {} loader1 = FileSystemLoader(self.base) loader2 = FunctionLoader(self._template_loader) loader = ChoiceLoader([loader1, loader2]) self.env = Environment(loader=loader, trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True, block_start_string=BLOCK_START, block_end_string=BLOCK_END, variable_start_string=VAR_START, variable_end_string=VAR_END, comment_start_string=COMMENT_START, comment_end_string=COMMENT_END, undefined=StrictUndefined) # adding variables self.variables['env'] = os.environ if variables: self.variables.update(variables) # adding header method self.env.globals['header'] = self._header # adding helper methods self.log.dbg('load global functions:') self._load_funcs_to_dic(jhelpers, self.env.globals) if func_file: for ffile in func_file: self.log.dbg(f'load custom functions from {ffile}') self._load_path_to_dic(ffile, self.env.globals) if filter_file: for ffile in filter_file: self.log.dbg(f'load custom filters from {ffile}') self._load_path_to_dic(ffile, self.env.filters) if self.debug: self._debug_dict('template additional variables', variables) def generate(self, src): """ render template from path may raise a UndefinedException in case a variable is undefined """ if not os.path.exists(src): return '' try: return self._handle_file(src) except UndefinedError as exc: err = f'undefined variable: {exc.message}' raise UndefinedException(err) from exc def generate_string(self, string): """ render template from string may raise a UndefinedException in case a variable is undefined """ if not string: return '' try: return self.env.from_string(string).render(self.variables) except UndefinedError as exc: err = f'undefined variable: {exc.message}' raise UndefinedException(err) from exc def add_tmp_vars(self, newvars=None): """add vars to the globals, make sure to call restore_vars""" saved_variables = self.variables.copy() if not newvars: return saved_variables self.variables.update(newvars) return saved_variables def restore_vars(self, saved_globals): """restore globals from add_tmp_vars""" self.variables = saved_globals.copy() def update_variables(self, variables): """update variables""" self.variables.update(variables) def _load_path_to_dic(self, path, dic): mod = utils.get_module_from_path(path) if not mod: self.log.warn(f'cannot load module \"{path}\"') return self._load_funcs_to_dic(mod, dic) def _load_funcs_to_dic(self, mod, dic): """dynamically load functions from module to dic""" if not mod or not dic: return funcs = utils.get_module_functions(mod) for name, func in funcs: self.log.dbg(f'load function \"{name}\"') dic[name] = func @classmethod def _header(cls, prepend=''): """add a comment usually in the header of a dotfile""" return f'{prepend}{utils.header()}' def _handle_file(self, src): """generate the file content from template""" try: # pylint: disable=C0415 import magic filetype = magic.from_file(src, mime=True) self.log.dbg('using \"magic\" for filetype identification') except ImportError: # fallback _, filetype = utils.run(['file', '-b', '--mime-type', src], debug=self.debug) self.log.dbg('using \"file\" for filetype identification') filetype = filetype.strip() istext = self._is_text(filetype) self.log.dbg(f'filetype \"{src}\": {filetype}') self.log.dbg(f'is text \"{src}\": {istext}') if not istext: return self._handle_bin_file(src) return self._handle_text_file(src) @classmethod def _is_text(cls, fileoutput): """return if `file -b` output is ascii text""" out = fileoutput.lower() if out.startswith('text'): return True if 'empty' in out: return True if 'json' in out: return True return False def _template_loader(self, relpath): """manually load template when outside of base""" path = os.path.join(self.base, relpath) path = os.path.normpath(path) if not os.path.exists(path): raise TemplateNotFound(path) with open(path, 'r', encoding='utf8') as file: content = file.read() return content def _handle_text_file(self, src): """write text to file""" template_rel_path = os.path.relpath(src, self.base) try: template = self.env.get_template(template_rel_path) content = template.render(self.variables) except UnicodeDecodeError: data = self._read_bad_encoded_text(src) content = self.generate_string(data) return content.encode('utf-8') def _handle_bin_file(self, src): """write binary to file""" # this is dirty if not src.startswith(self.base): src = os.path.join(self.base, src) with open(src, 'rb') as file: content = file.read() return content @classmethod def _read_bad_encoded_text(cls, path): """decode non utf-8 data""" with open(path, 'rb') as file: data = file.read() return data.decode('utf-8', 'replace') @staticmethod def is_template(path, ignore=None, debug=False): """recursively check if any file is a template within path""" if debug: LOG.dbg(f'is template: {path}', force=True) path = os.path.expanduser(path) if not os.path.exists(path): # does not exist return False if utils.must_ignore([path], ignore, debug=debug): # must be ignored return False if os.path.isfile(path): # is file return Templategen._is_template(path, ignore=ignore, debug=debug) # is a directory for entry in os.listdir(path): fpath = os.path.join(path, entry) if not os.path.isfile(fpath): # recursively explore directory if Templategen.is_template(fpath, ignore=ignore, debug=debug): return True else: # check if file is a template if Templategen._is_template(fpath, ignore=ignore, debug=debug): return True return False @staticmethod def var_is_template(string): """check if variable contains template(s)""" return VAR_START in str(string) @staticmethod def _is_template(path, ignore, debug=False): """test if file pointed by path is a template""" if utils.must_ignore([path], ignore, debug=debug): return False if not os.path.isfile(path): return False if os.stat(path).st_size == 0: return False markers = [BLOCK_START, VAR_START, COMMENT_START] patterns = [re.compile(marker.encode()) for marker in markers] try: with io.open(path, "r", encoding="utf-8") as file: mapf = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) for pattern in patterns: if pattern.search(mapf): return True except UnicodeDecodeError: # is binary so surely no template return False return False def _debug_dict(self, title, elems): """pretty print dict""" if not self.debug: return self.log.dbg(f'{title}:') if not elems: return for k, val in elems.items(): self.log.dbg(f' - \"{k}\": {val}') dotdrop-1.12.9/dotdrop/updater.py000066400000000000000000000410611436430751200167570ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 handle the update of dotfiles """ import os import shutil import filecmp import fnmatch # local imports from dotdrop.logger import Logger from dotdrop.templategen import Templategen from dotdrop.utils import patch_ignores, removepath, get_unique_tmp_name, \ write_to_tmpfile, must_ignore, mirror_file_rights, get_file_perm from dotdrop.exceptions import UndefinedException TILD = '~' class Updater: """dotfiles updater""" def __init__(self, dotpath, variables, conf, profile_key, dry=False, safe=True, debug=False, ignore=None, showpatch=False, ignore_missing_in_dotdrop=False): """constructor @dotpath: path where dotfiles are stored @variables: dictionary of variables for the templates @conf: configuration manager @profile_key: the profile key @dry: simulate @safe: ask for overwrite if True @debug: enable debug @ignore: pattern to ignore when updating @showpatch: show patch if dotfile to update is a template """ self.dotpath = dotpath self.variables = variables self.conf = conf self.profile_key = profile_key self.dry = dry self.safe = safe self.debug = debug self.ignore = ignore or [] self.ignores = None self.showpatch = showpatch self.ignore_missing_in_dotdrop = ignore_missing_in_dotdrop self.templater = Templategen(variables=self.variables, base=self.dotpath, debug=self.debug) # save template vars self.tvars = self.templater.add_tmp_vars() self.log = Logger(debug=self.debug) def update_path(self, path): """update the dotfile installed on path""" path = os.path.expanduser(path) if not os.path.lexists(path): self.log.err(f'\"{path}\" does not exist!') return False dotfiles = self.conf.get_dotfile_by_dst(path, profile_key=self.profile_key) if not dotfiles: return False for dotfile in dotfiles: if not dotfile: err = f'invalid dotfile for update: {dotfile.key}' self.log.err(err) return False msg = f'updating {dotfile} from path \"{path}\"' self.log.dbg(msg) if not self._update(path, dotfile): return False return True def update_key(self, key): """update the dotfile referenced by key""" dotfile = self.conf.get_dotfile(key, profile_key=self.profile_key) if not dotfile: self.log.dbg(f'no such dotfile: \"{key}\"') msg = f'invalid dotfile for update: {key}' self.log.err(msg) return False self.log.dbg(f'updating {dotfile} from key \"{key}\"') path = self.conf.path_to_dotfile_dst(dotfile.dst) return self._update(path, dotfile) def _update(self, path, dotfile): """update dotfile from file pointed by path""" ret = False new_path = None ignores = list(set(self.ignore + dotfile.upignore)) self.ignores = patch_ignores(ignores, dotfile.dst, debug=self.debug) self.log.dbg(f'ignore pattern(s): {self.ignores}') deployed_path = os.path.expanduser(path) local_path = os.path.join(self.dotpath, dotfile.src) local_path = os.path.expanduser(local_path) if not os.path.exists(deployed_path): msg = f'\"{deployed_path}\" does not exist' self.log.err(msg) return False if not os.path.exists(local_path): msg = f'\"{local_path}\" does not exist, import it first' self.log.err(msg) return False ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \ dotfile.ignore_missing_in_dotdrop if (ignore_missing_in_dotdrop and not os.path.exists(local_path)) or \ self._ignore([deployed_path, local_path]): self.log.sub(f'\"{dotfile.key}\" ignored') return True # apply write transformation if any new_path = self._apply_trans_w(deployed_path, dotfile) if not new_path: return False # save current rights deployed_mode = get_file_perm(deployed_path) local_mode = get_file_perm(local_path) # handle the pointed file if os.path.isdir(new_path): ret = self._handle_dir(new_path, local_path, dotfile) else: ret = self._handle_file(new_path, local_path) if deployed_mode != local_mode: # mirror rights msg = f'adopt mode {deployed_mode:o} for {dotfile.key}' self.log.dbg(msg) if self.conf.update_dotfile(dotfile.key, deployed_mode): ret = True # clean temporary files if new_path != deployed_path and os.path.exists(new_path): removepath(new_path, logger=self.log) return ret def _apply_trans_w(self, path, dotfile): """apply write transformation to dotfile""" trans = dotfile.get_trans_w() if not trans: return path self.log.dbg(f'executing write transformation {trans}') tmp = get_unique_tmp_name() self.templater.restore_vars(self.tvars) newvars = dotfile.get_dotfile_variables() self.templater.add_tmp_vars(newvars=newvars) if not trans.transform(path, tmp, templater=self.templater, debug=self.debug): if os.path.exists(tmp): removepath(tmp, logger=self.log) err = f'transformation \"{trans.key}\" failed for {dotfile.key}' self.log.err(err) return None return tmp def _is_template(self, path): if not Templategen.is_template(path, ignore=self.ignores, debug=self.debug): self.log.dbg(f'{path} is NO template') return False self.log.warn(f'{path} uses template, update manually') return True def _show_patch(self, fpath, tpath): """provide a way to manually patch the template""" content = self._resolve_template(tpath) tmp = write_to_tmpfile(content) mirror_file_rights(tpath, tmp) cmds = ['diff', '-u', tmp, fpath, '|', 'patch', tpath] cmdss = ' '.join(cmds) self.log.warn(f'try patching with: \"{cmdss}\"') return False def _resolve_template(self, tpath): """resolve the template to a temporary file""" self.templater.restore_vars(self.tvars) return self.templater.generate(tpath) def _same_rights(self, left, right): """return True if files have the same modes""" try: lefts = get_file_perm(left) rights = get_file_perm(right) return lefts == rights except OSError as exc: self.log.err(exc) return False def _mirror_rights(self, src, dst): srcr = get_file_perm(src) dstr = get_file_perm(dst) if srcr == dstr: return msg = f'copy rights from {src} ({srcr:o}) to {dst} ({dstr:o})' self.log.dbg(msg) try: mirror_file_rights(src, dst) except OSError as exc: self.log.err(exc) def _handle_file(self, deployed_path, local_path, compare=True): """sync path (deployed file) and local_path (dotdrop dotfile path)""" if self._ignore([deployed_path, local_path]): self.log.sub(f'\"{local_path}\" ignored') return True self.log.dbg(f'update for file {deployed_path} and {local_path}') if self._is_template(local_path): # dotfile is a template self.log.dbg(f'{local_path} is a template') if self.showpatch: try: self._show_patch(deployed_path, local_path) except UndefinedException as exc: msg = f'unable to show patch for {deployed_path}: {exc}' self.log.warn(msg) return False if compare and \ filecmp.cmp(deployed_path, local_path, shallow=False) and \ self._same_rights(deployed_path, local_path): # no difference self.log.dbg(f'identical files: {deployed_path} and {local_path}') return True if not self._overwrite(deployed_path, local_path): return False try: if self.dry: self.log.dry(f'would cp {deployed_path} {local_path}') else: self.log.dbg(f'cp {deployed_path} {local_path}') shutil.copyfile(deployed_path, local_path) self._mirror_rights(deployed_path, local_path) self.log.sub(f'\"{local_path}\" updated') except IOError as exc: self.log.warn(f'{deployed_path} update failed, do manually: {exc}') return False return True def _handle_dir(self, deployed_path, local_path, dotfile): """sync path (local dir) and local_path (dotdrop dir path)""" self.log.dbg(f'handle update for dir {deployed_path} to {local_path}') # paths must be absolute (no tildes) deployed_path = os.path.expanduser(deployed_path) local_path = os.path.expanduser(local_path) if self._ignore([deployed_path, local_path]): self.log.sub(f'\"{local_path}\" ignored') return True # find the differences diff = filecmp.dircmp(deployed_path, local_path, ignore=None) # handle directories diff ret = self._merge_dirs(diff, dotfile) self._mirror_rights(deployed_path, local_path) return ret def _merge_dirs_create_left_only(self, diff, left, right, ignore_missing_in_dotdrop): """create dirs that don't exist in dotdrop""" for toadd in diff.left_only: exist = os.path.join(left, toadd) if not os.path.isdir(exist): # ignore files for now continue # match to dotdrop dotpath new = os.path.join(right, toadd) if (ignore_missing_in_dotdrop and not os.path.exists(new)) or \ self._ignore([exist, new]): self.log.sub(f'\"{exist}\" ignored') continue if self.dry: self.log.dry(f'would cp -r {exist} {new}') continue self.log.dbg(f'cp -r {exist} {new}') # Newly created directory should be copied as is (for efficiency). def ign(src, names): whitelist, blacklist = set(), set() for ignore in self.ignores: for name in names: path = os.path.join(src, name) if ignore.startswith('!') and \ fnmatch.fnmatch(path, ignore[1:]): # add to whitelist whitelist.add(name) elif fnmatch.fnmatch(path, ignore): # add to blacklist blacklist.add(name) return blacklist - whitelist try: shutil.copytree(exist, new, ignore=ign) except OSError as exc: msg = f'error copying dir {exist}' self.log.err(f'{msg}: {exc}') continue self.log.sub(f'\"{new}\" dir added') def _merge_dirs_remove_right_only(self, diff, left, right, ignore_missing_in_dotdrop): """remove dirs that don't exist in deployed version""" for toremove in diff.right_only: old = os.path.join(right, toremove) if not os.path.isdir(old): # ignore files for now continue if self._ignore([old]): continue if self.dry: self.log.dry(f'would rm -r {old}') continue self.log.dbg(f'rm -r {old}') if not self._confirm_rm_r(old): continue removepath(old, logger=self.log) self.log.sub(f'\"{old}\" dir removed') # handle files diff # sync files that exist in both but are different fdiff = diff.diff_files fdiff.extend(diff.funny_files) fdiff.extend(diff.common_funny) for file in fdiff: fleft = os.path.join(left, file) fright = os.path.join(right, file) if (ignore_missing_in_dotdrop and not os.path.exists(fright)) or \ self._ignore([fleft, fright]): continue if self.dry: self.log.dry(f'would cp {fleft} {fright}') continue self.log.dbg(f'cp {fleft} {fright}') self._handle_file(fleft, fright, compare=False) def _merge_dirs_copy_left_only(self, diff, left, right, ignore_missing_in_dotdrop): """copy files that don't exist in dotdrop""" for toadd in diff.left_only: exist = os.path.join(left, toadd) if os.path.isdir(exist): # ignore dirs, done above continue new = os.path.join(right, toadd) if (ignore_missing_in_dotdrop and not os.path.exists(new)) or \ self._ignore([exist, new]): continue if self.dry: self.log.dry(f'would cp {exist} {new}') continue self.log.dbg(f'cp {exist} {new}') try: shutil.copyfile(exist, new) except OSError as exc: msg = f'error copying file {exist}' self.log.err(f'{msg}: {exc}') continue self._mirror_rights(exist, new) self.log.sub(f'\"{new}\" added') def _merge_dirs_remove_right_only_2(self, diff, right): """remove files that don't exist in deployed version""" for toremove in diff.right_only: new = os.path.join(right, toremove) if not os.path.exists(new): continue if os.path.isdir(new): # ignore dirs, done above continue if self._ignore([new]): continue if self.dry: self.log.dry(f'would rm {new}') continue self.log.dbg(f'rm {new}') removepath(new, logger=self.log) self.log.sub(f'\"{new}\" removed') def _merge_dirs(self, diff, dotfile): """Synchronize directories recursively.""" left, right = diff.left, diff.right self.log.dbg(f'sync dir {left} to {right}') if self._ignore([left, right]): return True ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \ dotfile.ignore_missing_in_dotdrop self._merge_dirs_create_left_only(diff, left, right, ignore_missing_in_dotdrop) self._merge_dirs_remove_right_only(diff, left, right, ignore_missing_in_dotdrop) self._merge_dirs_copy_left_only(diff, left, right, ignore_missing_in_dotdrop) self._merge_dirs_remove_right_only_2(diff, right) # compare rights for common in diff.common_files: leftf = os.path.join(left, common) rightf = os.path.join(right, common) if not self._same_rights(leftf, rightf): self._mirror_rights(leftf, rightf) # Recursively decent into common subdirectories. for subdir in diff.subdirs.values(): self._merge_dirs(subdir, dotfile) # Nothing more to do here. return True def _overwrite(self, src, dst): """ask for overwritting""" msg = f'Overwrite \"{dst}\" with \"{src}\"?' if self.safe and not self.log.ask(msg): return False return True def _confirm_rm_r(self, directory): """ask for rm -r directory""" msg = f'Recursively remove \"{directory}\"?' if self.safe and not self.log.ask(msg): return False return True def _ignore(self, paths): if must_ignore(paths, self.ignores, debug=self.debug): self.log.dbg(f'ignoring update for {paths}') return True return False dotdrop-1.12.9/dotdrop/utils.py000066400000000000000000000346221436430751200164600ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 utilities """ import subprocess import tempfile import os import uuid import fnmatch import inspect import importlib import filecmp import itertools import shutil import json import requests from packaging import version # local import from dotdrop.logger import Logger from dotdrop.exceptions import UnmetDependency from dotdrop.version import __version__ as VERSION LOG = Logger() STAR = '*' # the environment variable for temporary ENV_TEMP = 'DOTDROP_TMPDIR' # the temporary directory TMPDIR = None # files dotdrop refuses to remove DONOTDELETE = [ os.path.expanduser('~'), os.path.expanduser('~/.config'), ] NOREMOVE = [os.path.normpath(p) for p in DONOTDELETE] def run(cmd, debug=False): """run a command (expects a list)""" if debug: fcmd = ' '.join(cmd) LOG.dbg(f'exec: {fcmd}', force=True) with subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as proc: out, _ = proc.communicate() ret = proc.returncode out = out.splitlines(keepends=True) lines = ''.join([x.decode('utf-8', 'replace') for x in out]) return ret == 0, lines def write_to_tmpfile(content): """write some content to a tmp file""" path = get_tmpfile() with open(path, 'wb') as file: file.write(content) return path def shellrun(cmd, debug=False): """ run a command in the shell (expects a string) returns True|False, output """ if debug: LOG.dbg(f'shell exec: \"{cmd}\"', force=True) ret, out = subprocess.getstatusoutput(cmd) if debug: LOG.dbg(f'shell result ({ret}): {out}', force=True) return ret == 0, out def userinput(prompt, debug=False): """ get user input return user input """ if debug: LOG.dbg(f'get user input for \"{prompt}\"', force=True) pre = f'Please provide the value for \"{prompt}\": ' res = input(pre) if debug: LOG.dbg(f'user input result: {res}', force=True) return res def fastdiff(left, right): """fast compare files and returns True if different""" return not filecmp.cmp(left, right, shallow=False) def diff(original, modified, diff_cmd='', debug=False): """compare two files, returns '' if same""" if not diff_cmd: diff_cmd = 'diff -r -u {0} {1}' replacements = { "{0}": original, "{original}": original, "{1}": modified, "{modified}": modified, } cmd = [replacements.get(x, x) for x in diff_cmd.split()] _, out = run(cmd, debug=debug) return out def get_tmpdir(): """create and return the temporary directory""" # pylint: disable=W0603 global TMPDIR # pylint: enable=W0603 if TMPDIR: return TMPDIR tmp = _get_tmpdir() TMPDIR = tmp return tmp def _get_tmpdir(): """create the tmpdir""" try: if ENV_TEMP in os.environ: tmp = os.environ[ENV_TEMP] tmp = os.path.expanduser(tmp) tmp = os.path.abspath(tmp) tmp = os.path.normpath(tmp) os.makedirs(tmp, exist_ok=True) return tmp except OSError: pass return tempfile.mkdtemp(prefix='dotdrop-') def get_tmpfile(): """create a temporary file""" tmpdir = get_tmpdir() return tempfile.NamedTemporaryFile(prefix='dotdrop-', dir=tmpdir, delete=False).name def get_unique_tmp_name(): """get a unique file name (not created)""" unique = str(uuid.uuid4()) tmpdir = get_tmpdir() return os.path.join(tmpdir, unique) def removepath(path, logger=None): """ remove a file/directory/symlink if logger is defined, OSError are catched and printed to logger.warn instead of being forwarded as OSError """ if not path: return if not os.path.lexists(path): err = f'File not found: {path}' if logger: logger.warn(err) return raise OSError(err) if os.path.normpath(os.path.expanduser(path)) in NOREMOVE: err = f'Dotdrop refuses to remove {path}' if logger: logger.warn(err) return LOG.err(err) raise OSError(err) try: if os.path.islink(path) or os.path.isfile(path): os.unlink(path) elif os.path.isdir(path): shutil.rmtree(path) else: err = f'Unsupported file type for deletion: {path}' raise OSError(err) except Exception as exc: err = str(exc) if logger: logger.warn(err) return raise OSError(err) from exc def samefile(path1, path2): """return True if represent the same file""" if not os.path.exists(path1): return False if not os.path.exists(path2): return False return os.path.samefile(path1, path2) def header(): """return dotdrop header""" return 'This dotfile is managed using dotdrop' def content_empty(string): """return True if is empty or only one CRLF""" if not string: return True if string == b'\n': return True return False def strip_home(path): """properly strip $HOME from path""" home = os.path.expanduser('~') + os.sep if path.startswith(home): path = path[len(home):] return path def must_ignore(paths, ignores, debug=False): """return true if any paths in list matches any ignore patterns""" if not ignores: return False if debug: LOG.dbg(f'must ignore? \"{paths}\" against {ignores}', force=True) ignored_negative, ignored = categorize( lambda ign: ign.startswith('!'), ignores) for path in paths: ignore_matches = [] # First ignore dotfiles for i in ignored: if fnmatch.fnmatch(path, i): if debug: LOG.dbg(f'ignore \"{i}\" match: {path}', force=True) ignore_matches.append(path) # Then remove any matches that actually shouldn't be ignored for nign in ignored_negative: # Each of these will start with an '!' so we need to remove that nign = nign[1:] if debug: msg = f'trying to match :\"{path}\" ' msg += f'with non-ignore-pattern:\"{nign}\"' LOG.dbg(msg, force=True) if fnmatch.fnmatch(path, nign): if debug: msg = f'negative ignore \"{nign}\" match: {path}' LOG.dbg(msg, force=True) try: ignore_matches.remove(path) except ValueError: warn = 'no files that are currently being ' warn += f'ignored match \"{nign}\". In order ' warn += 'for a negative ignore pattern ' warn += 'to work, it must match a file ' warn += 'that is being ignored by a ' warn += 'previous ignore pattern.' LOG.warn(warn) if ignore_matches: if debug: LOG.dbg(f'ignoring {paths}', force=True) return True if debug: LOG.dbg(f'NOT ignoring {paths}', force=True) return False def uniq_list(a_list): """unique elements of a list while preserving order""" new = [] if not a_list: return new for elem in a_list: if elem not in new: new.append(elem) return new def patch_ignores(ignores, prefix, debug=False): """allow relative ignore pattern""" new = [] LOG.dbg(f'ignores before patching: {ignores}', force=debug) for ignore in ignores: negative = ignore.startswith('!') if negative: ignore = ignore[1:] if os.path.isabs(ignore): # is absolute if negative: new.append('!' + ignore) else: new.append(ignore) continue if STAR in ignore: if ignore.startswith(STAR) or ignore.startswith(os.sep): # is glob if negative: new.append('!' + ignore) else: new.append(ignore) continue # patch ignore path = os.path.join(prefix, ignore) if negative: new.append('!' + path) else: new.append(path) LOG.dbg(f'ignores after patching: {new}', force=debug) return new def get_module_functions(mod): """return a list of fonction from a module""" funcs = [] for memb in inspect.getmembers(mod): name, func = memb if not inspect.isfunction(func): continue funcs.append((name, func)) return funcs def get_module_from_path(path): """get module from path""" if not path or not os.path.exists(path): return None module_name = os.path.basename(path).rstrip('.py') # allow any type of files importlib.machinery.SOURCE_SUFFIXES.append('') # import module spec = importlib.util.spec_from_file_location(module_name, path) mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(mod) return mod def dependencies_met(): """make sure all dependencies are met""" # check unix tools deps # diff command is checked in settings.py deps = ['file'] for dep in deps: if not shutil.which(dep): err = f'The tool \"{dep}\" was not found in the PATH!' raise UnmetDependency(err) # check python deps # pylint: disable=C0415 # python-magic name = 'python-magic' err = f'missing python module \"{name}\"' try: import magic assert magic if not hasattr(magic, 'from_file'): LOG.warn(err) except ImportError: LOG.warn(err) # docopt name = 'docopt' err = f'missing python module \"{name}\"' try: from docopt import docopt assert docopt except ImportError as exc: raise Exception(err) from exc # jinja2 name = 'jinja2' err = f'missing python module \"{name}\"' try: import jinja2 assert jinja2 except ImportError as exc: raise Exception(err) from exc # ruamel.yaml name = 'ruamel.yaml' err = f'missing python module \"{name}\"' try: from ruamel.yaml import YAML assert YAML except ImportError as exc: raise Exception(err) from exc # toml name = 'toml' err = f'missing python module \"{name}\"' try: import toml assert toml except ImportError as exc: raise Exception(err) from exc # distro name = 'distro' err = f'missing python module \"{name}\"' try: import distro assert distro except ImportError as exc: raise Exception(err) from exc # pylint: enable=C0415 def mirror_file_rights(src, dst): """mirror file rights of src to dst (can rise exc)""" if not os.path.exists(src) or not os.path.exists(dst): return rights = get_file_perm(src) os.chmod(dst, rights) def get_umask(): """return current umask value""" cur = os.umask(0) os.umask(cur) # return 0o777 - cur return cur def get_default_file_perms(path, umask): """get default rights for a file""" base = 0o666 if os.path.isdir(path): base = 0o777 return base - umask def get_file_perm(path): """return file permission""" if not os.path.exists(path): return 0o777 return os.stat(path, follow_symlinks=True).st_mode & 0o777 def chmod(path, mode, debug=False): """change mode of file""" if debug: LOG.dbg(f'chmod {mode:o} {path}', force=True) os.chmod(path, mode) return get_file_perm(path) == mode def adapt_workers(options, logger): """adapt number of workers if safe/dry""" if options.safe and options.workers > 1: logger.warn('workers set to 1 when --force is not used') options.workers = 1 if options.dry and options.workers > 1: logger.warn('workers set to 1 when --dry is used') options.workers = 1 def categorize(function, iterable): """separate an iterable into elements for which function(element) is true for each element and for which function(element) is false for each element""" return (tuple(filter(function, iterable)), tuple(itertools.filterfalse(function, iterable))) def debug_list(title, elems, debug): """pretty print list""" if not debug: return LOG.dbg(f'{title}:', force=debug) for elem in elems: LOG.dbg(f'\t- {elem}', force=debug) def debug_dict(title, elems, debug): """pretty print dict""" if not debug: return LOG.dbg(f'{title}:', force=debug) for k, val in elems.items(): if isinstance(val, list): LOG.dbg(f'\t- \"{k}\":', force=debug) for i in val: LOG.dbg(f'\t\t- {i}', force=debug) else: LOG.dbg(f'\t- \"{k}\": {val}', force=debug) def check_version(): """check dotdrop version on github and compare with current""" url = 'https://api.github.com/repos/deadc0de6/dotdrop/releases/latest' req = requests.get(url, timeout=1) if not req: return try: latest = req.json()['name'] except json.decoder.JSONDecodeError: return except ValueError: return if latest.startswith('v'): latest = latest[1:] if version.parse(VERSION) < version.parse(latest): msg = f'A new version of dotdrop is available ({latest})' LOG.warn(msg) def pivot_path(path, newdir, striphome=False, logger=None): """change path to be under newdir""" if logger: logger.dbg(f'pivot new dir: \"{newdir}\"') logger.dbg(f'strip home: {striphome}') if striphome: path = strip_home(path) sub = path.lstrip(os.sep) new = os.path.join(newdir, sub) if logger: logger.dbg(f'pivot \"{path}\" to \"{new}\"') return new def is_bin_in_path(command): """ check binary from command is in path """ bpath = "" if not command: return False try: binary = command.split(" ")[0] except ValueError: return False if not binary: return False try: bpath = shutil.which(binary) except shutil.Error: return False if not bpath: return False return os.path.exists(bpath) dotdrop-1.12.9/dotdrop/version.py000066400000000000000000000001571436430751200170010ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2018, deadc0de6 """ __version__ = '1.12.9' dotdrop-1.12.9/highlighters/000077500000000000000000000000001436430751200157455ustar00rootroot00000000000000dotdrop-1.12.9/highlighters/README.md000066400000000000000000000002051436430751200172210ustar00rootroot00000000000000Highlighters for dotdrop templates * [kakoune](https://kakoune.org/) -> [dotdrop.kak](/highlighters/kakoune/dotdrop.kak) (see #305) dotdrop-1.12.9/highlighters/kakoune/000077500000000000000000000000001436430751200174025ustar00rootroot00000000000000dotdrop-1.12.9/highlighters/kakoune/dotdrop.kak000066400000000000000000000014741436430751200215530ustar00rootroot00000000000000hook global WinCreate .* %{ require-module python add-highlighter window/dotdrop regions add-highlighter window/dotdrop/expression region '\{\{@[@]' '[@]@\}\}' group add-highlighter window/dotdrop/statement region '\{%@[@]' '[@]@%\}' group add-highlighter window/dotdrop/comment region '\{#@[@]' '[@]@#\}' fill comment add-highlighter window/dotdrop/expression/ fill variable add-highlighter window/dotdrop/statement/ fill variable add-highlighter window/dotdrop/expression/ ref python add-highlighter window/dotdrop/statement/ ref python add-highlighter window/dotdrop/expression/ regex '\{\{@[@]|[@]@\}\}' 0:block add-highlighter window/dotdrop/statement/ regex '\{%@[@]|[@]@%\}' 0:block add-highlighter window/dotdrop/statement/ regex 'endfor|endif' 0:keyword } dotdrop-1.12.9/manpage/000077500000000000000000000000001436430751200146745ustar00rootroot00000000000000dotdrop-1.12.9/manpage/dotdrop.1000066400000000000000000000132071436430751200164340ustar00rootroot00000000000000.\" Text automatically generated by txt2man .TH dotdrop 1 "25 January 2023" "dotdrop-1.12.9" "Save your dotfiles once, deploy them everywhere" .SH NAME \fBdotdrop \fP- save your dotfiles once, deploy them everywhere \fB .SH SYNOPSIS .nf .fam C \fBdotdrop\fP \fICOMMAND\fP [\fIOPTIONS\fP] [\fIARGS\fP] \.\.\. \fBdotdrop\fP \fB--help\fP \fBdotdrop\fP \fB--version\fP .fam T .fi .fam T .fi .SH DESCRIPTION Dotdrop makes the management of dotfiles between different hosts easy. It allows you to store your dotfiles in Git and automagically deploy different versions of the same file on different setups. .PP It also allows manage different sets of dotfiles. For example, you can have a set of dotfiles for your home laptop and a different set for your office desktop. Those sets may overlap, and different versions of the same dotfiles can be deployed using different predefined profiles. Or you may have a main set of dotfiles for your everyday host and a subset you only need to deploy to temporary hosts (cloud VM etc.) that may be using a slightly different version of some of the dotfiles. .SH COMMANDS .TP .B install Install dotfiles .RS .TP .B \fB-a\fP \fB--force-actions\fP Execute all actions even if no dotfile is installed. .TP .B \fB-d\fP \fB--dry\fP Dry run. .TP .B \fB-D\fP \fB--showdiff\fP Show a diff before overwriting. .TP .B \fB-f\fP \fB--force\fP Do not ask user confirmation for anything. .TP .B \fB-n\fP \fB--nodiff\fP Do not diff when installing. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .TP .B \fB-t\fP \fB--temp\fP Install to a temporary directory for review. .TP .B \fB-w\fP \fB--workers\fP= Number of concurrent workers [default: 1]. .TP .B \fB-W\fP \fB--workdir-clear\fP Clear the workdir. .RE .TP .B import Import dotfiles .RS .TP .B \fB-d\fP \fB--dry\fP Dry run. .TP .B \fB-f\fP \fB--force\fP Do not ask user confirmation for anything. .TP .B \fB-i\fP \fB--ignore\fP= Pattern to ignore. .TP .B \fB-l\fP \fB--link\fP= Link option (nolink|absolute|relative|link_children). .TP .B \fB-m\fP \fB--preserve-mode\fP Insert a chmod entry in the dotfile with its mode. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .TP .B \fB-s\fP \fB--as\fP= Import as a different path from actual path. .TP .B \fB--transr\fP= Associate trans_read key on import. .TP .B \fB--transw\fP= Apply trans_write key on import. .RE .TP .B compare Compare dotfiles .RS .TP .B \fB-C\fP \fB--file\fP= Path of dotfile to compare. .TP .B \fB-i\fP \fB--ignore\fP= Pattern to ignore. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .TP .B \fB-w\fP \fB--workers\fP= Number of concurrent workers [default: 1]. .TP .B \fB-z\fP \fB--ignore-missing\fP Ignore files in installed folders that are missing. .RE .TP .B update Update a managed dotfile .RS .TP .B \fB-d\fP \fB--dry\fP Dry run. .TP .B \fB-f\fP \fB--force\fP Do not ask user confirmation for anything. .TP .B \fB-i\fP \fB--ignore\fP= Pattern to ignore. .TP .B \fB-k\fP \fB--key\fP Treat as a dotfile key. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .TP .B \fB-P\fP \fB--show-patch\fP Provide a one-liner to manually patch template. .TP .B \fB-w\fP \fB--workers\fP= Number of concurrent workers [default: 1]. .TP .B \fB-z\fP \fB--ignore-missing\fP Ignore files in installed folders that are missing. .RE .TP .B remove Unmanage a dotfile .RS .TP .B \fB-d\fP \fB--dry\fP Dry run. .TP .B \fB-f\fP \fB--force\fP Do not ask user confirmation for anything. .TP .B \fB-k\fP \fB--key\fP Treat as a dotfile key. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .RE .TP .B files List the managed dotfiles .RS .TP .B \fB-G\fP \fB--grepable\fP Grepable output. .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .TP .B \fB-T\fP \fB--template\fP Only template dotfiles. .RE .TP .B detail Detail managed dotfiles .RS .TP .B \fB-p\fP \fB--profile\fP= Specify the profile to use. .RE .TP .B profiles List all profiles .RS .TP .B \fB-G\fP \fB--grepable\fP Grepable output. .SH GLOBAL OPTIONS .TP .B \fB-b\fP \fB--no-banner\fP Do not display the banner. .TP .B \fB-c\fP \fB--cfg\fP= Path to the config. .TP .B \fB-V\fP \fB--verbose\fP Be verbose. .SH EXAMPLES .TP .B \fBdotdrop\fP install [\fB-VbtfndDaW\fP] [\fB-c\fP ] [\fB-p\fP ] [\fB-w\fP ] [\.\.\.] .TP .B \fBdotdrop\fP import [\fB-Vbdfm\fP] [\fB-c\fP ] [\fB-p\fP ] [\fB-i\fP \.\.\.] [\fB--transr\fP=] [\fB--transw\fP=] [\fB-l\fP ] [\fB-s\fP ] \.\.\. .TP .B \fBdotdrop\fP compare [\fB-LVbz\fP] [\fB-c\fP ] [\fB-p\fP ] [\fB-w\fP ] [\fB-C\fP \.\.\.] [\fB-i\fP \.\.\.] .TP .B \fBdotdrop\fP update [\fB-VbfdkPz\fP] [\fB-c\fP ] [\fB-p\fP ] [\fB-w\fP ] [\fB-i\fP \.\.\.] [\.\.\.] .TP .B \fBdotdrop\fP remove [\fB-Vbfdk\fP] [\fB-c\fP ] [\fB-p\fP ] [\.\.\.] .TP .B \fBdotdrop\fP files [\fB-VbTG\fP] [\fB-c\fP ] [\fB-p\fP ] .TP .B \fBdotdrop\fP detail [\fB-Vb\fP] [\fB-c\fP ] [\fB-p\fP ] [\.\.\.] .TP .B \fBdotdrop\fP profiles [\fB-VbG\fP] [\fB-c\fP ] .PP \fBdotdrop\fP \fB--help\fP .PP \fBdotdrop\fP \fB--version\fP .SH ABOUT More information can be found on the repository under and in the main documentation under .SH AUTHOR Written by deadc0de6 .SH COPYRIGHT \fBdotdrop\fP is copyright (c) 2017, deadc0de6. Released under the GPLv3 license. .SH REPORTING BUGS dotdrop-1.12.9/manpage/dotdrop.txt2man000066400000000000000000000120011436430751200176600ustar00rootroot00000000000000NAME dotdrop - save your dotfiles once, deploy them everywhere SYNOPSIS dotdrop COMMAND [OPTIONS] [ARGS] ... dotdrop --help dotdrop --version DESCRIPTION Dotdrop makes the management of dotfiles between different hosts easy. It allows you to store your dotfiles in Git and automagically deploy different versions of the same file on different setups. It also allows manage different sets of dotfiles. For example, you can have a set of dotfiles for your home laptop and a different set for your office desktop. Those sets may overlap, and different versions of the same dotfiles can be deployed using different predefined profiles. Or you may have a main set of dotfiles for your everyday host and a subset you only need to deploy to temporary hosts (cloud VM etc.) that may be using a slightly different version of some of the dotfiles. COMMANDS install Install dotfiles -a --force-actions Execute all actions even if no dotfile is installed. -d --dry Dry run. -D --showdiff Show a diff before overwriting. -f --force Do not ask user confirmation for anything. -n --nodiff Do not diff when installing. -p --profile= Specify the profile to use. -t --temp Install to a temporary directory for review. -w --workers= Number of concurrent workers [default: 1]. -W --workdir-clear Clear the workdir. import Import dotfiles -d --dry Dry run. -f --force Do not ask user confirmation for anything. -i --ignore= Pattern to ignore. -l --link= Link option (nolink|absolute|relative|link_children). -m --preserve-mode Insert a chmod entry in the dotfile with its mode. -p --profile= Specify the profile to use. -s --as= Import as a different path from actual path. --transr= Associate trans_read key on import. --transw= Apply trans_write key on import. compare Compare dotfiles -C --file= Path of dotfile to compare. -i --ignore= Pattern to ignore. -p --profile= Specify the profile to use. -w --workers= Number of concurrent workers [default: 1]. -z --ignore-missing Ignore files in installed folders that are missing. update Update a managed dotfile -d --dry Dry run. -f --force Do not ask user confirmation for anything. -i --ignore= Pattern to ignore. -k --key Treat as a dotfile key. -p --profile= Specify the profile to use. -P --show-patch Provide a one-liner to manually patch template. -w --workers= Number of concurrent workers [default: 1]. -z --ignore-missing Ignore files in installed folders that are missing. remove Unmanage a dotfile -d --dry Dry run. -f --force Do not ask user confirmation for anything. -k --key Treat as a dotfile key. -p --profile= Specify the profile to use. files List the managed dotfiles -G --grepable Grepable output. -p --profile= Specify the profile to use. -T --template Only template dotfiles. detail Detail managed dotfiles -p --profile= Specify the profile to use. profiles List all profiles -G --grepable Grepable output. GLOBAL OPTIONS -b --no-banner Do not display the banner. -c --cfg= Path to the config. -V --verbose Be verbose. EXAMPLES dotdrop install [-VbtfndDaW] [-c ] [-p ] [-w ] [...] dotdrop import [-Vbdfm] [-c ] [-p ] [-i ...] [--transr=] [--transw=] [-l ] [-s ] ... dotdrop compare [-LVbz] [-c ] [-p ] [-w ] [-C ...] [-i ...] dotdrop update [-VbfdkPz] [-c ] [-p ] [-w ] [-i ...] [...] dotdrop remove [-Vbfdk] [-c ] [-p ] [...] dotdrop files [-VbTG] [-c ] [-p ] dotdrop detail [-Vb] [-c ] [-p ] [...] dotdrop profiles [-VbG] [-c ] dotdrop --help dotdrop --version ABOUT More information can be found on the repository under and in the main documentation under AUTHOR Written by deadc0de6 COPYRIGHT dotdrop is copyright (c) 2017, deadc0de6. Released under the GPLv3 license. REPORTING BUGS dotdrop-1.12.9/manpage/generate.sh000077500000000000000000000014121436430751200170230ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2023, deadc0de6 # get current working directory rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") # extract version from git latest tag #version=$(git describe --tags --abbrev=0) # extract version from version.py version=$(grep version "${cur}"/../dotdrop/version.py | sed 's/^.*= .\(.*\).$/\1/g') if ! hash txt2man 2>/dev/null; then echo "install txt2man" exit 1 fi txt2man \ -t "dotdrop" \ -P "dotdrop" \ -r "dotdrop-${version}" \ -s 1 \ -v "Save your dotfiles once, deploy them everywhere" \ "${cur}/dotdrop.txt2man" > "${cur}/dotdrop.1" dotdrop-1.12.9/mkdocs.yml000066400000000000000000000042201436430751200152650ustar00rootroot00000000000000site_name: dotdrop site_url: https://github.com/deadc0de6/dotdrop repo_url: https://github.com/deadc0de6/dotdrop theme: name: readthedocs highlightjs: true hljs_languages: - yaml #collapse_navigation: false use_directory_urls: true nav: - 'Dotdrop': 'README.md' - 'Installation': 'installation.md' - 'Getting started': 'getting-started.md' - 'Usage': 'usage.md' - 'Config': - 'Config file': 'config/config-file.md' - 'Config block': 'config/config-config.md' - 'Dotfiles block': 'config/config-dotfiles.md' - 'Profiles block': 'config/config-profiles.md' - 'Actions block': 'config/config-actions.md' - 'Transformations block': 'config/config-transformations.md' - 'Variables block': 'config/config-variables.md' - 'Dynvariables block': 'config/config-dynvars.md' - 'Uservariables block': 'config/config-uservars.md' - 'Templating': - 'Templating': 'template/templating.md' - 'Template variables': 'template/template-variables.md' - 'Template methods': 'template/template-methods.md' - 'Template filters': 'template/template-filters.md' - 'Debugging templates': 'template/template-debug.md' - 'HowTo': - 'Append text to a dotfile on install': 'howto/append.md' - 'Create files on install': 'howto/create-special-files.md' - 'Group hosts and meta profiles': 'howto/group-hosts.md' - 'Handle compressed directories': 'howto/store-compressed-directories.md' - 'Handle secrets': 'howto/sensitive-dotfiles.md' - 'Handle special chars': 'howto/special-chars.md' - 'Improve Git integration': 'howto/improve-git-integration.md' - 'Include files or templates in templates': 'howto/include-in-template.md' - 'Manage system dotfiles': 'howto/system-config-files.md' - 'Merge files on install': 'howto/merge-files-when-installing.md' - 'Prompt user for variables': 'howto/prompt-user-for-variables.md' - 'Share content across dotfiles': 'howto/sharing-content.md' - 'Symlink dotfiles': 'howto/symlink-dotfiles.md' - '': '' - '': '' markdown_extensions: - meta - tables - fenced_code - toc: permalink: True extra_css: - css/extra.css dotdrop-1.12.9/packages/000077500000000000000000000000001436430751200150425ustar00rootroot00000000000000dotdrop-1.12.9/packages/arch-dotdrop-git/000077500000000000000000000000001436430751200202115ustar00rootroot00000000000000dotdrop-1.12.9/packages/arch-dotdrop-git/PKGBUILD000066400000000000000000000021761436430751200213430ustar00rootroot00000000000000# Maintainer: deadc0de6 _pkgname=dotdrop pkgname="${_pkgname}-git" pkgver=1.3.7.r13.g18b156e pkgrel=5 pkgdesc="Save your dotfiles once, deploy them everywhere " arch=('any') url="https://github.com/deadc0de6/dotdrop" license=('GPL') groups=() depends=('python' 'python-setuptools' 'python-jinja' 'python-docopt' 'python-ruamel-yaml' 'python-magic' 'python-requests' 'python-packaging' 'python-toml' 'python-distro') makedepends=('git') provides=(dotdrop) conflicts=(dotdrop) source=("git+https://github.com/deadc0de6/dotdrop.git") md5sums=('SKIP') pkgver() { cd "${_pkgname}" git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//g' } package() { cd "${_pkgname}" python setup.py install --root="${pkgdir}/" --optimize=1 install -Dm644 ${srcdir}/${_pkgname}/completion/dotdrop-completion.bash "${pkgdir}/usr/share/bash-completion/completions/${_pkgname}" install -Dm644 ${srcdir}/${_pkgname}/completion/_dotdrop-completion.zsh "${pkgdir}/usr/share/zsh/site-functions/_${_pkgname}" install -Dm644 ${srcdir}/${_pkgname}/completion/dotdrop.fish "${pkgdir}/usr/share/fish/completions/${_pkgname}.fish" } dotdrop-1.12.9/packages/arch-dotdrop/000077500000000000000000000000001436430751200174305ustar00rootroot00000000000000dotdrop-1.12.9/packages/arch-dotdrop/PKGBUILD000066400000000000000000000020371436430751200205560ustar00rootroot00000000000000# Maintainer: deadc0de6 pkgname=dotdrop pkgver=1.5.4 pkgrel=1 pkgdesc="Save your dotfiles once, deploy them everywhere " arch=('any') url="https://github.com/deadc0de6/dotdrop" license=('GPL') groups=() depends=('python' 'python-setuptools' 'python-jinja' 'python-docopt' 'python-ruamel-yaml' 'python-magic' 'python-requests' 'python-packaging' 'python-toml' 'python-distro') makedepends=('git') source=("git+https://github.com/deadc0de6/dotdrop.git#tag=v${pkgver}") md5sums=('SKIP') pkgver() { cd "${pkgname}" git describe --abbrev=0 --tags | sed 's/^v//g' } package() { cd "${pkgname}" python setup.py install --root="${pkgdir}/" --optimize=1 install -Dm644 ${srcdir}/${pkgname}/completion/dotdrop-completion.bash "${pkgdir}/usr/share/bash-completion/completions/${pkgname}" install -Dm644 ${srcdir}/${pkgname}/completion/_dotdrop-completion.zsh "${pkgdir}/usr/share/zsh/site-functions/_${pkgname}" install -Dm644 ${srcdir}/${pkgname}/completion/dotdrop.fish "${pkgdir}/usr/share/fish/completions/${pkgname}.fish" } dotdrop-1.12.9/packages/genpkg.sh000077500000000000000000000017411436430751200166570ustar00rootroot00000000000000#!/bin/bash # author: deadc0de6 # # update packages # # $1: version up() { # update pkgver [ "${1}" != "" ] && sed -i "s/^pkgver=.*$/pkgver=${1}/g" ${pkgfile} # create srcinfo rm -f .SRCINFO makepkg --printsrcinfo > .SRCINFO } # pivot rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi # cur cur=`dirname $(${rl} "${0}")` opwd=`pwd` pkgfile="PKGBUILD" cd ${cur} ######################## # update arch package # tag release ######################## dir="arch-dotdrop" echo "doing ${dir} ..." cd ${dir} version="`git describe --abbrev=0 --tags | sed 's/^v//g'`" up ${version} cd ${OLDPWD} ######################### ## update arch package ## git release ######################### #dir="arch-dotdrop-git" #echo "doing ${dir} ..." #cd ${dir} ## replace pkgver ##version="`git describe --long --tags | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//g'`" #up #cd ${OLDPWD} # pivot back cd ${opwd} dotdrop-1.12.9/packages/snap/000077500000000000000000000000001436430751200160035ustar00rootroot00000000000000dotdrop-1.12.9/packages/snap/snapcraft.yaml000066400000000000000000000026361436430751200206570ustar00rootroot00000000000000name: dotdrop base: core18 adopt-info: version summary: Save your dotfiles once, deploy them everywhere description: | Dotdrop makes the management of dotfiles between different hosts easy. It allows to store your dotfiles on git and automagically deploy different versions of the same file on different setups. It also allows to manage different sets of dotfiles. For example you can have a set of dotfiles for your home laptop and a different set for your office desktop. Those sets may overlap and different versions of the same dotfiles can be deployed on different predefined profiles. Or you may have a main set of dotfiles for your everyday's host and a sub-set you only need to deploy to temporary hosts (cloud VM, etc) that may be using a slightly different version of some of the dotfiles. It allows to store your dotfiles on git and automagically deploy different versions of the same file on different setups. Project page: https://github.com/deadc0de6/dotdrop confinement: strict grade: stable apps: dotdrop: command: dotdrop plugs: - home parts: dotdrop: plugin: python python-version: python3 source: ../../ stage-packages: - file version: source: . plugin: nil override-build: | VERSION=$(git describe --abbrev=0 --tags | sed 's/^v//g') snapcraftctl set-version "${VERSION}" dotdrop-1.12.9/requirements.txt000066400000000000000000000004061436430751200165500ustar00rootroot00000000000000Jinja2; python_version > '3.5' docopt; python_version > '3.5' ruamel.yaml; python_version > '3.5' python-magic; python_version > '3.5' packaging; python_version > '3.5' requests; python_version > '3.5' toml; python_version > '3.5' distro; python_version > '3.5' dotdrop-1.12.9/scripts/000077500000000000000000000000001436430751200147535ustar00rootroot00000000000000dotdrop-1.12.9/scripts/change-link.py000077500000000000000000000023761436430751200175200ustar00rootroot00000000000000#!/usr/bin/env python3 """ author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2018, deadc0de6 change the `link` key in all dotfiles to a specific value usage example: ./change-link.py --true ../config.yaml --ignore f_vimrc --ignore f_xinitrc """ import os import io from docopt import docopt from ruamel.yaml import YAML as yaml USAGE = """ change-link.py Usage: change-link.py --value= [--ignore=...] change-link.py --help Options: -h --help Show this screen. """ KEY = 'dotfiles' ENTRY = 'link' def change_link(path, value, ignores): """change link value""" with open(path, 'r', encoding='utf-8') as file: content = yaml(typ='safe').load(file) for k, val in content[KEY].items(): if k in ignores: continue val[ENTRY] = value output = io.StringIO() data = yaml() data.default_flow_style = False data.indent = 2 data.typ = 'rt' data.dump(content, output) print(output) def main(): """entry point""" args = docopt(USAGE) path = os.path.expanduser(args['']) value = args['--value'] ignores = args['--ignore'] change_link(path, value, ignores) if __name__ == '__main__': main() dotdrop-1.12.9/scripts/check_links.py000077500000000000000000000045461436430751200176160ustar00rootroot00000000000000#!/usr/bin/env python3 """ author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2023, deadc0de6 URL checking script """ import sys import re from urllib.parse import urlparse import requests TIMEOUT = 3 VALID_RET = [ 200, 302, ] IGNORES = [ 'badgen.net', ] USER_AGENT = ( 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/58.0.3029.110 Safari/537.36' ) HEADERS = { 'User-Agent': USER_AGENT, } PATTERN = ( r"https?://[a-zA-Z0-9][a-zA-Z0-9-]{1,61}" r"[a-zA-Z0-9]\.[=a-zA-Z0-9\_\/\?\&\%\+\#\.\-]+" ) def get_links(path): """get a list of URLS""" with open(path, encoding='utf-8') as file: content = file.read() entries = re.findall(PATTERN, content) urls = list(set(entries)) return urls def check_links(urls): """check urls""" cnt = 0 ign = 0 for url in urls: cnt += 1 hostname = urlparse(url).hostname if hostname in IGNORES: print(f' [IGN] {url}') ign += 1 continue verb = 'head' ret = requests.head(url, timeout=TIMEOUT, allow_redirects=True, headers=HEADERS).status_code if ret not in VALID_RET: verb = 'get' ret = requests.get(url, timeout=TIMEOUT, allow_redirects=True, headers=HEADERS).status_code if ret not in VALID_RET: print(f' [ERROR] {url} returned {ret}') return False print(f' [OK-{verb}-{ret}] {url}') print(f'OK - total {cnt} links checked ({ign} ignored)') return True if __name__ == '__main__': if len(sys.argv) < 2: print(f'usage: {sys.argv[0]} ') sys.exit(1) print(f'checking {sys.argv[1]} for links...') links = get_links(sys.argv[1]) print(f' found {len(links)} links') try: if not check_links(links): sys.exit(1) except ValueError as exc: print(f'error {exc}') sys.exit(1) except urlparse.URLError as exc: print(f'urlparse error {exc}') sys.exit(1) except requests.exceptions.RequestException as exc: print(f'requests error {exc}') sys.exit(1) sys.exit(0) dotdrop-1.12.9/scripts/dotdrop-version-manager.sh000077500000000000000000000124301436430751200220600ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 api="https://api.github.com/repos" pro="deadc0de6/dotdrop" logs="/tmp/dotdrop-version.log" ######################## ## git operations ######################## git_get_changes() { #git fetch origin git remote update >>${logs} 2>&1 } get_current_commit() { git rev-parse --short HEAD } # get current tag get_current_tag() { git describe --tags 2>/dev/null } get_current_branch() { git branch --show-current 2>/dev/null } is_on_release() { git describe --exact-match --tags HEAD >>${logs} 2>&1 echo "$?" } checkout() { git checkout "$1" >>${logs} 2>&1 } ######################## ## api operations ######################## # get latest release get_latest() { curl "${api}/${pro}/releases/latest" 2>>${logs} | grep 'tag_name' | sed 's/^.*: "\(.*\)",/\1/g' } # list all releases get_releases() { curl "${api}/${pro}/releases" 2>>${logs} | grep 'tag_name' | sed 's/^.*: "\(.*\)",/\1/g' } # list all branches get_branches() { curl "${api}/${pro}/branches" 2>>${logs} | grep name | sed 's/^.*: "\(.*\)",/\1/g' } ######################## ## print status ######################## # get current status get_current() { local tag local commit local branch echo "current version:" stable=$(is_on_release) if [ "${stable}" = "0" ]; then echo -e "\ton stable" tag=$(get_current_tag) echo -e "\trelease version: ${tag}" else echo -e "\ton unstable" tag=$(get_current_tag) echo -e "\ttag: ${tag}" commit=$(get_current_commit) echo -e "\tcommit: ${commit}" fi branch=$(get_current_branch) [ "$branch" != "" ] && echo -e "\tbranch: ${branch}" } # check if new stable release is available need_update_stable() { local last local cur local tag git fetch origin >>${logs} 2>&1 last=$(get_latest) cur=$(get_current_tag) # get short tag if on a lightweight tag tag=$(echo "$cur" | sed 's/\(v.*\)-[0-9]*.-.*$/\1/g') if [ "${tag}" != "${last}" ]; then echo "new stable version available: ${last}" && exit 1 fi echo "your version is up-to-date" } # check if new updates are available need_update() { local changes git fetch origin >>${logs} 2>&1 # compare changes=$(git log HEAD..origin --oneline) [ "${changes}" != "" ] && echo "new updates available" && exit 1 echo "your version is up-to-date" } ######################## ## change operations ######################## checkout_last_tag() { local last local cur last=$(get_latest) [ "${last}" = "" ] && echo "unable to get last release" && return cur=$(get_current_tag) [ "${cur}" = "${last}" ] && return git_get_changes && checkout "${last}" } # $1: tag checkout_tag() { local cur cur=$(get_current_tag) [ "${cur}" = "${1}" ] && return git_get_changes && checkout "${1}" } checkout_branch() { git_get_changes && checkout "${1}" && git pull origin "${1}" >/dev/null 2>&1 } ######################## ## helpers ######################## # move to base of dotdrop move_to_base() { local curpwd curpwd=$(pwd) git submodule | grep dotdrop >/dev/null 2>&1 if [ "$?" ]; then # dotdrop is a submodule #echo "dotdrop used as a submodule" cd dotdrop || (echo "cannot change directory to dotdrop" && exit 1) return fi if [ -e dotdrop/version.py ]; then grep deadc0de6 dotdrop/version.py >/dev/null 2>&1 if [ "$?" ]; then # dotdrop is in current dir #echo "dotdrop is in current directory" return fi fi echo "dotdrop wasn't found" exit 1 } # print usage usage() { echo "$(basename "${0}") print current : print the version you are on" echo "$(basename "${0}") print releases : list available stable releases" echo "$(basename "${0}") print branches : list available branches" echo "$(basename "${0}") check unstable : check for new unstable updates" echo "$(basename "${0}") check stable : check for new stable release" echo "$(basename "${0}") get stable : change to latest stable release" echo "$(basename "${0}") get unstable : change to latest unstable" echo "$(basename "${0}") get version : change to a specific version" echo "$(basename "${0}") get branch : change to a specific branch" exit 1 } ######################## ## entry point ######################## [ "$1" = "" ] && usage [ "$1" != "print" ] && \ [ "$1" != "check" ] && \ [ "$1" != "get" ] && \ usage [ "$2" = "" ] && usage move_to_base if [ "$1" = "print" ]; then if [ "$2" = "current" ]; then get_current elif [ "$2" = "releases" ]; then get_releases elif [ "$2" = "branches" ]; then get_branches else usage fi elif [ "$1" = "check" ]; then if [ "$2" = "stable" ]; then get_current need_update_stable elif [ "$2" = "unstable" ]; then get_current need_update else usage fi elif [ "$1" = "get" ]; then if [ "$2" = "stable" ]; then checkout_last_tag get_current elif [ "$2" = "unstable" ]; then checkout_branch master get_current elif [ "$2" = "branch" ]; then [ "$3" = "" ] && usage checkout_branch "${3}" get_current elif [ "$2" = "version" ]; then [ "$3" = "" ] && usage checkout_tag "${3}" get_current else usage fi fi cd "${curpwd}" || true exit 0 dotdrop-1.12.9/scripts/yaml-to-toml.py000077500000000000000000000023151436430751200176640ustar00rootroot00000000000000#!/usr/bin/env python3 """ author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2022, deadc0de6 convert yaml config file to toml """ import sys # pip3 install ruamel.yaml from ruamel.yaml import YAML as yaml # pip3 install toml import toml def yaml_load(path): """load from yaml""" with open(path, 'r', encoding='utf8') as file: cont = yaml() cont.typ = 'rt' content = cont.load(file) return content def replace_none(content): """replace any occurence of None with empty string""" n = {} for k in content: if content[k] is None: if k == 'dotfiles': continue if k == 'profiles': continue n[k] = "" continue if isinstance(content[k], dict): n[k] = replace_none(content[k]) continue n[k] = content[k] return n def toml_dump(content): """dump toml to stdout""" return toml.dumps(content) if __name__ == '__main__': if len(sys.argv) < 2: print(f"usage: {sys.argv[0]} ") sys.exit(1) data = yaml_load(sys.argv[1]) data = replace_none(data) out = toml_dump(data) print(out) dotdrop-1.12.9/setup.py000066400000000000000000000035021436430751200147760ustar00rootroot00000000000000"""setup.py""" from os import path from setuptools import setup, find_packages from dotdrop.version import __version__ as VERSION README = 'README.md' here = path.abspath(path.dirname(__file__)) def read_readme(readme_path): """read readme content""" with open(readme_path, encoding="utf-8") as file: return file.read() REQUIRES_PYTHON = '>=3' URL = f'https://github.com/deadc0de6/dotdrop/archive/v{VERSION}.tar.gz' setup( name='dotdrop', version=VERSION, description='Save your dotfiles once, deploy them everywhere', long_description=read_readme(README), long_description_content_type="text/markdown; variant=GFM", url='https://github.com/deadc0de6/dotdrop', download_url=URL, options={"bdist_wheel": {"python_tag": "py3"}}, # include anything from MANIFEST.in include_package_data=True, author='deadc0de6', author_email='deadc0de6@foo.bar', license='GPLv3', python_requires=REQUIRES_PYTHON, classifiers=[ 'Development Status :: 5 - Production/Stable', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', ], keywords='dotfiles jinja2', packages=find_packages(exclude=['tests*']), install_requires=[ 'docopt', 'Jinja2', 'ruamel.yaml', 'python-magic', 'packaging', 'requests', 'toml', 'distro'], extras_require={ 'dev': ['check-manifest'], 'test': ['coverage', 'pytest', 'pytest-cov'], }, entry_points={ 'console_scripts': [ 'dotdrop=dotdrop:main', ], }, ) dotdrop-1.12.9/test-doc.sh000077500000000000000000000012331436430751200153440ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 ## test doc external links find . -type f -iname '*.md' | while read -r line; do ./scripts/check_links.py "${line}" done ## test the doc internal links ## https://github.com/remarkjs/remark-validate-links ## https://github.com/tcort/markdown-link-check ## npm install -g remark-cli remark-validate-links set +e which remark >/dev/null 2>&1 r="$?" set -e if [ "$r" != "0" ]; then echo "[WARNING] install \"remark\" to test the doc" exit 1 fi find . -type f -iname '*.md' | while read -r line; do remark -f -u validate-links "${line}" done echo "documentation OK" dotdrop-1.12.9/test-ng.sh000077500000000000000000000020631436430751200152050ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2023, deadc0de6 # stop on first error set -e rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi cur=`dirname $(${rl} "${0}")` tmpworkdir="/tmp/dotdrop-tests-workdir" export DOTDROP_WORKDIR="${tmpworkdir}" workers=${DOTDROP_WORKERS} if [ ! -z ${workers} ]; then DOTDROP_WORKERS=${workers} echo "ENABLE workers: ${workers}" fi # run bash tests export DOTDROP_DEBUG="yes" unset DOTDROP_FORCE_NODEBUG workdir_tmp_exists="no" [ -d "~/.config/dotdrop/tmp" ] && workdir_tmp_exists="yes" if [ -z ${GITHUB_WORKFLOW} ]; then ## local export COVERAGE_FILE= tests-ng/tests-launcher.py else ## CI/CD export COVERAGE_FILE="${cur}/.coverage" # running multiple jobs in parallel sometimes # messes with the results on remote servers tests-ng/tests-launcher.py 1 fi # clear workdir [ "${workdir_tmp_exists}" = "no" ] && rm -rf ~/.config/dotdrop/tmp # clear temp workdir rm -rf "${tmpworkdir}"dotdrop-1.12.9/test-syntax.sh000077500000000000000000000036651436430751200161400ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # stop on first error #set -ev set -e # versions echo "pylint version:" pylint --version echo "pycodestyle version:" pycodestyle --version echo "pyflakes version:" pyflakes --version # PEP8 tests which pycodestyle >/dev/null 2>&1 [ "$?" != "0" ] && echo "Install pycodestyle" && exit 1 echo "testing with pycodestyle" # W503: Line break occurred before a binary operator # W504: Line break occurred after a binary operator pycodestyle --ignore=W503,W504 dotdrop/ pycodestyle tests/ pycodestyle scripts/ # pyflakes tests echo "testing with pyflakes" pyflakes dotdrop/ pyflakes tests/ pyflakes scripts/ # pylint echo "testing with pylint" # https://pylint.pycqa.org/en/latest/user_guide/checkers/features.html # R0902: too-many-instance-attributes # R0913: too-many-arguments # R0903: too-few-public-methods # R0914: too-many-locals # R0915: too-many-statements # R0912: too-many-branches # R0911: too-many-return-statements # R0904: too-many-public-methods pylint \ --disable=R0902 \ --disable=R0913 \ --disable=R0903 \ --disable=R0914 \ --disable=R0915 \ --disable=R0912 \ --disable=R0911 \ --disable=R0904 \ dotdrop/ # C0103: invalid-name pylint \ --disable=R0902 \ --disable=R0913 \ --disable=R0903 \ --disable=R0914 \ --disable=R0915 \ --disable=R0912 \ --disable=R0911 \ --disable=R0904 \ --disable=C0103 \ scripts/ set +e exceptions="save_uservariables_name\|@@\|diff_cmd\|original,\|modified," # f-string errors and missing f literal find dotdrop/ -iname '*.py' -exec grep --with-filename -n -v "f'" {} \; | grep -v "{'" | grep -v "${exceptions}" | grep "'.*}" && echo "bad string format (1): ${errs}" && exit 1 find dotdrop/ -iname '*.py' -exec grep --with-filename -n -v 'f"' {} \; | grep -v "f'" | grep -v '{"' | grep -v "${exceptions}" | grep '".*}' && echo "bad string format (2): ${errs}" && exit 1 set -e echo "syntax OK"dotdrop-1.12.9/test-unittest.sh000077500000000000000000000015201436430751200164550ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2023, deadc0de6 # stop on first error set -e rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi cur=`dirname $(${rl} "${0}")` workers=${DOTDROP_WORKERS} if [ ! -z ${workers} ]; then unset DOTDROP_WORKERS echo "DISABLE workers" fi # execute tests with coverage if [ -z ${GITHUB_WORKFLOW} ]; then ## local export COVERAGE_FILE= # do not print debugs when running tests (faster) # tests PYTHONPATH="dotdrop" nose2 --with-coverage --coverage dotdrop --plugin=nose2.plugins.mp -N0 else ## CI/CD export COVERAGE_FILE="${cur}/.coverage" # tests PYTHONPATH="dotdrop" nose2 --with-coverage --coverage dotdrop fi #PYTHONPATH="dotdrop" python3 -m pytest testsdotdrop-1.12.9/tests-ng/000077500000000000000000000000001436430751200150305ustar00rootroot00000000000000dotdrop-1.12.9/tests-ng/README.md000066400000000000000000000005051436430751200163070ustar00rootroot00000000000000These are tests for testing entire behavior through shell scripts more easily than the overly complicated Python tests. For adding your own test, create a new script, copy the beginning of an existing test script (before the *this is the test*), and complete it with your test. Helper functions are available in `helpers`. dotdrop-1.12.9/tests-ng/actions-args-template.sh000077500000000000000000000071121436430751200215730ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test action template execution # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # Convenience function to grep into a file and exit with an erro message if the # content is not found should_grep() { SHOULD_GREP_STR="$1" SHOULD_GREP_FILE="$2" grep "$SHOULD_GREP_STR" "$SHOULD_GREP_FILE" > /dev/null || { echo >&2 "$SHOULD_GREP_STR not found in $SHOULD_GREP_FILE" exit 1 } unset SHOULD_GREP_FILE SHOULD_GREP_STR } # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: "echo {0} > {1}" post: postaction: "echo {0} > ${tmpa}/post" nakedaction: "echo {0} > ${tmpa}/naked" profileaction: "echo {0} >> ${tmpa}/profile" dynaction: "echo {0} > ${tmpa}/dyn" config: backup: true create: true dotpath: dotfiles default_actions: - preaction '{{@@ var_pre @@}}' "${tmpa}/pre" - postaction '{{@@ var_post @@}}' - nakedaction '{{@@ var_naked @@}}' dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc actions: - profileaction '{{@@ var_profile @@}}' - dynaction '{{@@ user_name @@}}' include: - p2 p2: dotfiles: - f_abc actions: - profileaction '{{@@ var_profile_2 @@}}' variables: var_profile_2: profile_var_2 variables: var_pre: pre_var var_post: post_var var_naked: naked_var var_profile: profile_var dynvariables: user_name: 'echo $USER' _EOF #cat ${cfg} # create the dotfile echo 'test' > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks action [ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 [ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 [ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 [ ! -e ${tmpa}/profile ] && echo 'profile action not executed' && exit 1 [ ! -e ${tmpa}/dyn ] && echo 'dynamic acton action not executed' && exit 1 should_grep pre_var ${tmpa}/pre should_grep post_var ${tmpa}/post should_grep naked_var ${tmpa}/naked should_grep profile_var ${tmpa}/profile should_grep profile_var_2 ${tmpa}/profile should_grep "$USER" ${tmpa}/dyn echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions-args.sh000077500000000000000000000056711436430751200177720ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test pre/post/naked actions with arguments # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmpa}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: echo '{0} {1}' > ${tmpa}/pre post: postaction: echo '{0} {1} {2}' > ${tmpa}/post nakedaction: echo '{0}' > ${tmpa}/naked emptyaction: echo 'empty' > ${tmpa}/empty tgtaction: echo 'tgt' > ${tmpa}/{0} config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction test1 test2 - postaction test3 test4 test5 - nakedaction "test6 something" - emptyaction - tgtaction tgt profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "test" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --verbose # checks [ ! -e ${tmpa}/pre ] && echo "pre arg action not found" && exit 1 grep test1 ${tmpa}/pre >/dev/null grep test2 ${tmpa}/pre >/dev/null [ ! -e ${tmpa}/post ] && echo "post arg action not found" && exit 1 grep test3 ${tmpa}/post >/dev/null grep test4 ${tmpa}/post >/dev/null grep test5 ${tmpa}/post >/dev/null [ ! -e ${tmpa}/naked ] && echo "naked arg action not found" && exit 1 grep "test6 something" ${tmpa}/naked >/dev/null [ ! -e ${tmpa}/empty ] && echo "empty arg action not found" && exit 1 grep empty ${tmpa}/empty >/dev/null [ ! -e ${tmpa}/tgt ] && echo "tgt arg action not found" && exit 1 grep tgt ${tmpa}/tgt >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions-default.sh000077500000000000000000000074541436430751200204630ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test default action execution # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: failpre: "false" preaction: echo 'pre' > ${tmpa}/pre preaction1: echo 'preinside' > ${tmpa}/preinside post: failpost: "false" postaction: echo 'post' > ${tmpa}/post postaction1: echo 'postinside' > ${tmpa}/postinside nakedaction: echo 'naked' > ${tmpa}/naked nakedaction1: echo 'nakedinside' > ${tmpa}/nakedinside appendaction: echo 'newline' >> ${tmpa}/append config: backup: true create: true dotpath: dotfiles default_actions: - preaction - postaction - nakedaction - appendaction dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction1 - nakedaction1 - postaction1 profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo 'test' > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks pre action [ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 [ ! -e ${tmpa}/preinside ] && echo 'pre action not executed' && exit 1 grep pre ${tmpa}/pre >/dev/null grep preinside ${tmpa}/preinside >/dev/null # checks post action [ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 [ ! -e ${tmpa}/postinside ] && echo 'post action not executed' && exit 1 grep post ${tmpa}/post >/dev/null grep postinside ${tmpa}/postinside >/dev/null # checks naked action [ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 [ ! -e ${tmpa}/nakedinside ] && echo 'naked action not executed' && exit 1 grep naked ${tmpa}/naked >/dev/null grep nakedinside ${tmpa}/nakedinside >/dev/null # test default action run cd ${ddpath} | ${bin} install -fa -c ${cfg} -p p1 -V cnt=`cat ${tmpa}/append | wc -l` [ "${cnt}" != "2" ] && echo "default_actions not run on -a" && exit 1 # clear rm -f ${tmpa}/naked* ${tmpa}/pre* ${tmpa}/post* ${tmpd}/abc cat > ${cfg} << _EOF actions: pre: failpre: "false" config: backup: true create: true dotpath: dotfiles default_actions: - failpre dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # ensure failing actions make the installation fail # install set +e cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V set -e [ -e ${tmpd}/abc ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions-empty-dir.sh000077500000000000000000000125031436430751200207400ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test pre/post/naked actions # returns 1 in case of error # # exit on first error set -ev # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: clearemptydir: find -L '{0}' -empty -xtype d -delete config: backup: true create: true dotpath: dotfiles dotfiles: f_dir1: dst: ${tmpd}/dir1 src: dir1 ignoreempty: true actions: - clearemptydir ${tmpd}/dir1 instignore: - '*ignore' f_dir2: dst: ${tmpd}/dir2 src: dir2 link: link_children ignoreempty: true actions: - clearemptydir ${tmpd}/dir2 instignore: - '*ignore' f_dir3: dst: ${tmpd}/dir3 src: dir3 link: link ignoreempty: true actions: - clearemptydir ${tmpd}/dir3 instignore: - '*ignore' profiles: p1: dotfiles: - f_dir1 - f_dir2 - f_dir3 _EOF #cat ${cfg} # create the dotfile mkdir ${tmps}/dotfiles/dir1 mkdir ${tmps}/dotfiles/dir1/empty echo "to-ignore" > ${tmps}/dotfiles/dir1/empty/this.ignore mkdir ${tmps}/dotfiles/dir1/not-empty echo "file" > ${tmps}/dotfiles/dir1/not-empty/file mkdir ${tmps}/dotfiles/dir1/sub mkdir ${tmps}/dotfiles/dir1/sub/empty echo "to-ignore-too" > ${tmps}/dotfiles/dir1/sub/empty/that.ignore # create the dotfile mkdir ${tmps}/dotfiles/dir2 mkdir ${tmps}/dotfiles/dir2/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir2/empty/this.ignore mkdir ${tmps}/dotfiles/dir2/not-empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir2/not-empty/file mkdir ${tmps}/dotfiles/dir2/sub mkdir ${tmps}/dotfiles/dir2/sub/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir2/sub/empty/that.ignore # create the dotfile mkdir ${tmps}/dotfiles/dir3 mkdir ${tmps}/dotfiles/dir3/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir3/empty/this.ignore mkdir ${tmps}/dotfiles/dir3/not-empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir3/not-empty/file mkdir ${tmps}/dotfiles/dir3/sub mkdir ${tmps}/dotfiles/dir3/sub/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir3/sub/empty/that.ignore # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks normal [ ! -d ${tmpd}/dir1 ] && exit 1 [ -d ${tmpd}/dir1/empty ] && exit 1 [ -e ${tmpd}/dir1/empty/this.ignore ] && exit 1 [ ! -d ${tmpd}/dir1/not-empty ] && exit 1 [ ! -e ${tmpd}/dir1/not-empty/file ] && exit 1 [ -d ${tmpd}/dir1/sub ] && exit 1 [ -d ${tmpd}/dir1/sub/empty ] && exit 1 [ -e ${tmpd}/dir1/sub/empty/that.ignore ] && exit 1 grep "file" ${tmpd}/dir1/not-empty/file # checks link_children [ ! -d ${tmpd}/dir2 ] && exit 1 [ ! -h ${tmpd}/dir2/empty ] && exit 1 [ -e ${tmpd}/dir2/empty/this.ignore ] && exit 1 [ ! -d ${tmpd}/dir2/not-empty ] && exit 1 [ ! -h ${tmpd}/dir2/not-empty ] && exit 1 [ ! -e ${tmpd}/dir2/not-empty/file ] && exit 1 [ -d ${tmpd}/dir2/sub ] && exit 1 [ -d ${tmpd}/dir2/sub/empty ] && exit 1 [ -e ${tmpd}/dir2/sub/empty/that.ignore ] && exit 1 grep "p1" ${tmpd}/dir2/not-empty/file # checks link [ ! -d ${tmpd}/dir3 ] && exit 1 [ ! -h ${tmpd}/dir3 ] && exit 1 [ -d ${tmpd}/dir3/empty ] && exit 1 [ -e ${tmpd}/dir3/empty/this.ignore ] && exit 1 [ ! -d ${tmpd}/dir3/not-empty ] && exit 1 [ ! -e ${tmpd}/dir3/not-empty/file ] && exit 1 [ -d ${tmpd}/dir3/sub ] && exit 1 [ -d ${tmpd}/dir3/sub/empty ] && exit 1 [ -e ${tmpd}/dir3/sub/empty/that.ignore ] && exit 1 grep "p1" ${tmpd}/dir3/not-empty/file # second install won't trigger the action cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # 2>&1 | tee ${tmpa}/log # check normal [ -d ${tmpd}/dir1/empty ] && echo "empty directory not cleaned" && exit 1 [ -d ${tmpd}/dir1/sub/empty ] && echo "empty directory not cleaned" && exit 1 # check link_children [ -d ${tmpd}/dir2/empty ] && echo "empty directory not cleaned" && exit 1 [ -d ${tmpd}/dir2/sub/empty ] && echo "empty directory not cleaned" && exit 1 # check link [ -d ${tmpd}/dir3/empty ] && echo "empty directory not cleaned" && exit 1 [ -d ${tmpd}/dir3/sub/empty ] && echo "empty directory not cleaned" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions-pre.sh000077500000000000000000000110321436430751200176100ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test pre action execution # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: failpre: "false" preaction: echo 'pre' > ${tmpa}/pre preaction2: echo 'pre2' > ${tmpa}/pre2 preaction3: echo 'pre3' > ${tmpa}/pre3 multiple: echo 'multiple' >> ${tmpa}/multiple multiple2: echo 'multiple2' >> ${tmpa}/multiple2 nakedaction: echo 'naked' > ${tmpa}/naked nakedaction2: echo 'naked2' > ${tmpa}/naked2 nakedaction3: echo 'naked3' > ${tmpa}/naked3 config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction - nakedaction f_fail: dst: ${tmpd}/fail src: fail actions: - failpre f_link: dst: ${tmpd}/link src: link link: true actions: - preaction2 - nakedaction2 d_dir: dst: ${tmpd}/dir src: dir actions: - multiple d_dlink: dst: ${tmpd}/dlink src: dlink link: true actions: - preaction3 - nakedaction3 - multiple2 profiles: p1: dotfiles: - f_abc - f_link - d_dir - d_dlink p2: dotfiles: - f_fail _EOF #cat ${cfg} # create the dotfile echo 'test' > ${tmps}/dotfiles/abc echo 'link' > ${tmps}/dotfiles/link echo 'fail' > ${tmps}/dotfiles/fail mkdir -p ${tmps}/dotfiles/dir echo 'test1' > ${tmps}/dotfiles/dir/file1 echo 'test2' > ${tmps}/dotfiles/dir/file2 mkdir -p ${tmps}/dotfiles/dlink echo 'test3' > ${tmps}/dotfiles/dlink/dfile1 echo 'test4' > ${tmps}/dotfiles/dlink/dfile2 # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 grep_or_fail pre ${tmpa}/pre [ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 grep_or_fail naked ${tmpa}/naked [ ! -e ${tmpa}/multiple ] && echo 'pre action multiple not executed' && exit 1 grep_or_fail multiple ${tmpa}/multiple [ "`wc -l ${tmpa}/multiple | awk '{print $1}'`" -gt "1" ] && echo 'pre action multiple executed twice' && exit 1 [ ! -e ${tmpa}/pre2 ] && echo 'pre action 2 not executed' && exit 1 grep_or_fail pre2 ${tmpa}/pre2 [ ! -e ${tmpa}/naked2 ] && echo 'naked action 2 not executed' && exit 1 grep_or_fail naked2 ${tmpa}/naked2 [ ! -e ${tmpa}/multiple2 ] && echo 'pre action multiple 2 not executed' && exit 1 grep_or_fail multiple2 ${tmpa}/multiple2 [ "`wc -l ${tmpa}/multiple2 | awk '{print $1}'`" -gt "1" ] && echo 'pre action multiple 2 executed twice' && exit 1 [ ! -e ${tmpa}/naked3 ] && echo 'naked action 3 not executed' && exit 1 grep_or_fail naked3 ${tmpa}/naked3 # remove the pre action result and re-install rm ${tmpa}/pre cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ -e ${tmpa}/pre ] && echo "pre exists" && exit 1 # ensure failing actions make the installation fail # install set +e cd ${ddpath} | ${bin} install -f -c ${cfg} -p p2 -V set -e [ -e ${tmpd}/fail ] && echo "fail exists" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions-template.sh000077500000000000000000000066411436430751200206470ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test action template execution # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: "echo {{@@ _dotfile_abs_src @@}} > {0}" post: postaction: "echo {{@@ _dotfile_abs_src @@}} > ${tmpa}/post" nakedaction: "echo {{@@ _dotfile_abs_src @@}} > ${tmpa}/naked" config: backup: true create: true dotpath: dotfiles default_actions: - preaction "${tmpa}/pre" - postaction - nakedaction dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo 'test' > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks action [ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 [ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 [ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 grep abc ${tmpa}/pre >/dev/null grep abc ${tmpa}/post >/dev/null grep abc ${tmpa}/naked >/dev/null # clear rm -f ${tmpa}/naked* ${tmpa}/pre* ${tmpa}/post* ${tmpd}/abc cat > ${cfg} << _EOF actions: pre: preaction: "echo {{@@ _dotfile_abs_dst @@}} > ${tmpa}/pre" post: postaction: "echo {{@@ _dotfile_abs_dst @@}} > ${tmpa}/post" nakedaction: "echo {{@@ _dotfile_abs_dst @@}} > ${tmpa}/naked" config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction - nakedaction - postaction profiles: p1: dotfiles: - f_abc _EOF # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks action [ ! -e ${tmpa}/pre ] && echo 'pre action not executed' && exit 1 [ ! -e ${tmpa}/post ] && echo 'post action not executed' && exit 1 [ ! -e ${tmpa}/naked ] && echo 'naked action not executed' && exit 1 grep "${tmpd}/abc" ${tmpa}/pre >/dev/null grep "${tmpd}/abc" ${tmpa}/post >/dev/null grep "${tmpd}/abc" ${tmpa}/naked >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/actions.sh000077500000000000000000000067271436430751200170430ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test pre/post/naked actions # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: echo 'pre' > ${tmpa}/pre preaction2: echo 'pre2' > ${tmpa}/pre2 fake_pre: echo 'fake pre' > ${tmpa}/fake_pre expandvariable: "myvar=xxx; echo \${{myvar}} > ${tmpa}/expandvariable" post: postaction: echo 'post' > ${tmpa}/post postaction2: echo 'post2' > ${tmpa}/post2 nakedaction: echo 'naked' > ${tmpa}/naked _silentaction: echo 'silent' fakeaction: echo 'fake' > ${tmpa}/fake config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction - postaction - nakedaction - preaction2 - postaction2 - _silentaction - expandvariable f_fake: dst: src: actions: - fakeaction - fake_pre profiles: p1: dotfiles: - f_abc - f_fake _EOF #cat ${cfg} # create the dotfile echo "test" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V 2>&1 | tee ${tmpa}/log # checks [ ! -e ${tmpa}/pre ] && exit 1 grep pre ${tmpa}/pre >/dev/null [ ! -e ${tmpa}/post ] && exit 1 grep post ${tmpa}/post >/dev/null [ ! -e ${tmpa}/naked ] && exit 1 grep naked ${tmpa}/naked >/dev/null [ ! -e ${tmpa}/pre2 ] && exit 1 grep pre2 ${tmpa}/pre2 >/dev/null [ ! -e ${tmpa}/post2 ] && exit 1 grep post ${tmpa}/post2 >/dev/null [ ! -e ${tmpa}/log ] && exit 1 grep "executing \"echo 'naked' > ${tmpa}/naked" ${tmpa}/log >/dev/null grep "executing \"echo 'silent'" ${tmpa}/log >/dev/null && false grep "executing silent action \"_silentaction\"" ${tmpa}/log >/dev/null [ ! -e ${tmpa}/expandvariable ] && exit 1 grep xxx ${tmpa}/expandvariable >/dev/null # fake action [ ! -e ${tmpa}/fake ] && echo 'fake post action not executed' && exit 1 grep fake ${tmpa}/fake >/dev/null [ ! -e ${tmpa}/fake_pre ] && echo 'fake pre action not executed' && exit 1 grep 'fake pre' ${tmpa}/fake_pre >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/bad-diff-cmd.sh000077500000000000000000000037151436430751200175720ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test bad diff cmd # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" clear_on_exit "${basedir}" # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles diff_command: xxxxxxxxx {0} {1} dotfiles: profiles: _EOF set +e cd ${ddpath} | ${bin} compare -c ${cfg} [ "$?" = "0" ] && exit 1 out=$(cd ${ddpath} | ${bin} compare -c ${cfg}) echo "${out}" | grep -i 'traceback' && exit 1 cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles diff_command: dotfiles: profiles: _EOF set +e cd ${ddpath} | ${bin} compare -c ${cfg} [ "$?" = "0" ] && exit 1 out=$(cd ${ddpath} | ${bin} compare -c ${cfg}) echo "${out}" | grep -i 'traceback' && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-compare.sh000077500000000000000000000060431436430751200201100ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test chmod on compare # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile dnormal="${tmpd}/dir_normal" mkdir -p ${dnormal} echo "dir_normal/f1" > ${dnormal}/file1 echo "dir_normal/f2" > ${dnormal}/file2 chmod 777 ${dnormal} dlink="${tmpd}/dir_link" mkdir -p ${dlink} echo "dir_link/f1" > ${dlink}/file1 echo "dir_link/f2" > ${dlink}/file2 chmod 777 ${dlink} dlinkchildren="${tmpd}/dir_link_children" mkdir -p ${dlinkchildren} echo "dir_linkchildren/f1" > ${dlinkchildren}/file1 echo "dir_linkchildren/f2" > ${dlinkchildren}/file2 chmod 777 ${dlinkchildren} fnormal="${tmpd}/filenormal" echo "filenormal" > ${fnormal} chmod 777 ${fnormal} flink="${tmpd}/filelink" echo "filelink" > ${flink} chmod 777 ${flink} echo "f777" > ${tmps}/dotfiles/f777 chmod 700 ${tmps}/dotfiles/f777 toimport="${dnormal} ${dlink} ${dlinkchildren} ${fnormal} ${flink}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_f777: src: f777 dst: ${tmpd}/f777 chmod: 777 profiles: p1: dotfiles: - f_f777 _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 # import for i in ${toimport}; do cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 ${i} done #cat ${cfg} # patch rights chmod 700 ${dnormal} chmod 700 ${dlink} chmod 700 ${dlinkchildren} chmod 700 ${fnormal} chmod 700 ${flink} set +e out=`cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 2>&1` cnt=$(echo "${out}" | grep 'modes differ' | wc -l) set -e [ "${cnt}" != "5" ] && echo "${out}" && echo "compare modes failed (${cnt}, expecting 5)" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-import.sh000077500000000000000000000122171436430751200177740ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test chmod on import # with files and directories # with different link # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 file chmod_to_umask() { u=`umask` u=`echo ${u} | sed 's/^0*//'` if [ -d ${1} ]; then v=$((777 - u)) else v=$((666 - u)) fi chmod ${v} ${1} } # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfiles dnormal="${tmpd}/dir_normal" mkdir -p ${dnormal} echo "dir_normal/f1" > ${dnormal}/file1 echo "dir_normal/f2" > ${dnormal}/file2 chmod 777 ${dnormal} dlink="${tmpd}/dir_link" mkdir -p ${dlink} echo "dir_link/f1" > ${dlink}/file1 echo "dir_link/f2" > ${dlink}/file2 chmod 777 ${dlink} dlinkchildren="${tmpd}/dir_link_children" mkdir -p ${dlinkchildren} echo "dir_linkchildren/f1" > ${dlinkchildren}/file1 echo "dir_linkchildren/f2" > ${dlinkchildren}/file2 chmod 777 ${dlinkchildren} fnormal="${tmpd}/filenormal" echo "filenormal" > ${fnormal} chmod 777 ${fnormal} flink="${tmpd}/filelink" echo "filelink" > ${flink} chmod 777 ${flink} toimport="${dnormal} ${dlink} ${dlinkchildren} ${fnormal} ${flink}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import without --preserve-mode for i in ${toimport}; do cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V ${i} done cat ${cfg} # list files cd ${ddpath} | ${bin} detail -c ${cfg} -p p1 -V tot=`echo ${toimport} | wc -w` cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "${tot}" ] && echo "not all chmod inserted (1)" && exit 1 ## with link cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # import without --preserve-mode and link for i in ${toimport}; do cd ${ddpath} | ${bin} import -c ${cfg} -l absolute -f -p p1 -V ${i} done cat ${cfg} # list files cd ${ddpath} | ${bin} detail -c ${cfg} -p p1 -V tot=`echo ${toimport} | wc -w` cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "${tot}" ] && echo "not all chmod inserted (2)" && exit 1 tot=`echo ${toimport} | wc -w` cnt=`cat ${cfg} | grep 'link: absolute' | wc -l` [ "${cnt}" != "${tot}" ] && echo "not all link inserted" && exit 1 ## --preserve-mode cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # import with --preserve-mode for i in ${toimport}; do chmod_to_umask ${i} cd ${ddpath} | ${bin} import -c ${cfg} -m -f -p p1 -V ${i} done cat ${cfg} # list files cd ${ddpath} | ${bin} detail -c ${cfg} -p p1 -V tot=`echo ${toimport} | wc -w` cnt=`cat ${cfg} | grep "chmod: " | wc -l` [ "${cnt}" != "${tot}" ] && echo "not all chmod inserted (3)" && exit 1 ## import normal cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # import without --preserve-mode for i in ${toimport}; do chmod_to_umask ${i} cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V ${i} done cat ${cfg} # list files cd ${ddpath} | ${bin} detail -c ${cfg} -p p1 -V cnt=`cat ${cfg} | grep chmod | wc -l` [ "${cnt}" != "0" ] && echo "chmod inserted but not needed" && exit 1 ## with config option chmod_on_import cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles chmod_on_import: true dotfiles: profiles: _EOF # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # import for i in ${toimport}; do chmod_to_umask ${i} cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V ${i} done cat ${cfg} # list files cd ${ddpath} | ${bin} detail -c ${cfg} -p p1 -V cat ${cfg} tot=`echo ${toimport} | wc -w` cnt=`cat ${cfg} | grep "chmod: " | wc -l` [ "${cnt}" != "${tot}" ] && echo "not all chmod inserted (3)" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-install.sh000077500000000000000000000146741436430751200201410ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test chmod on install # with files and directories # with different link # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 path # $2 rights has_rights() { echo "testing ${1} is ${2}" [ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1 local mode=`stat -L -c '%a' "$1"` [ "${mode}" != "$2" ] && echo "bad mode for `basename $1` (${mode} VS expected ${2})" && exit 1 true } get_file_mode() { u=`umask` u=`echo ${u} | sed 's/^0*//'` v=$((666 - u)) echo "${v}" } get_dir_mode() { u=`umask` u=`echo ${u} | sed 's/^0*//'` v=$((777 - u)) echo "${v}" } # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" echo 'f777' > ${tmps}/dotfiles/f777 chmod 700 ${tmps}/dotfiles/f777 echo 'link' > ${tmps}/dotfiles/link chmod 777 ${tmps}/dotfiles/link mkdir -p ${tmps}/dotfiles/dir echo "f1" > ${tmps}/dotfiles/dir/f1 echo "exists" > ${tmps}/dotfiles/exists chmod 644 ${tmps}/dotfiles/exists echo "exists" > ${tmpd}/exists chmod 644 ${tmpd}/exists echo "existslink" > ${tmps}/dotfiles/existslink chmod 777 ${tmps}/dotfiles/existslink chmod 644 ${tmpd}/exists mkdir -p ${tmps}/dotfiles/direxists echo "f1" > ${tmps}/dotfiles/direxists/f1 mkdir -p ${tmpd}/direxists echo "f1" > ${tmpd}/direxists/f1 chmod 644 ${tmpd}/direxists/f1 chmod 744 ${tmpd}/direxists mkdir -p ${tmps}/dotfiles/linkchildren echo "f1" > ${tmps}/dotfiles/linkchildren/f1 mkdir -p ${tmps}/dotfiles/linkchildren/d1 echo "f2" > ${tmps}/dotfiles/linkchildren/d1/f2 echo '{{@@ profile @@}}' > ${tmps}/dotfiles/symlinktemplate mkdir -p ${tmps}/dotfiles/symlinktemplatedir echo "{{@@ profile @@}}" > ${tmps}/dotfiles/symlinktemplatedir/t echo 'nomode' > ${tmps}/dotfiles/nomode cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles force_chmod: true dotfiles: f_f777: src: f777 dst: ${tmpd}/f777 chmod: 777 f_link: src: link dst: ${tmpd}/link chmod: 777 link: link d_dir: src: dir dst: ${tmpd}/dir chmod: 777 f_exists: src: exists dst: ${tmpd}/exists chmod: 777 f_existslink: src: existslink dst: ${tmpd}/existslink chmod: 777 link: link d_direxists: src: direxists dst: ${tmpd}/direxists chmod: 777 d_linkchildren: src: linkchildren dst: ${tmpd}/linkchildren chmod: 777 link: link_children f_symlinktemplate: src: symlinktemplate dst: ${tmpd}/symlinktemplate chmod: 777 link: link d_symlinktemplatedir: src: symlinktemplatedir dst: ${tmpd}/symlinktemplatedir chmod: 777 link: link f_nomode: src: nomode dst: ${tmpd}/nomode profiles: p1: dotfiles: - f_f777 - f_link - d_dir - f_exists - f_existslink - d_direxists - d_linkchildren - f_symlinktemplate - d_symlinktemplatedir - f_nomode p2: dotfiles: - f_exists - d_linkchildren - f_symlinktemplate - f_nomode _EOF #cat ${cfg} # install echo "first install round" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V echo "first install round" has_rights "${tmpd}/f777" "777" has_rights "${tmpd}/link" "777" has_rights "${tmpd}/dir" "777" has_rights "${tmpd}/exists" "777" has_rights "${tmpd}/existslink" "777" has_rights "${tmpd}/direxists" "777" has_rights "${tmpd}/direxists/f1" "644" has_rights "${tmpd}/linkchildren" "777" has_rights "${tmpd}/linkchildren/f1" "644" has_rights "${tmpd}/linkchildren/d1" "755" has_rights "${tmpd}/linkchildren/d1/f2" "644" has_rights "${tmpd}/symlinktemplate" "777" m=`get_file_mode` has_rights "${tmpd}/nomode" "${m}" grep 'p1' ${tmpd}/symlinktemplate grep 'p1' ${tmpd}/symlinktemplatedir/t ## second round echo "exists" > ${tmps}/dotfiles/exists chmod 600 ${tmps}/dotfiles/exists echo "exists" > ${tmpd}/exists chmod 600 ${tmpd}/exists chmod 700 ${tmpd}/linkchildren chmod 600 ${tmpd}/symlinktemplate echo "second install round" cd ${ddpath} | ${bin} install -c ${cfg} -p p2 -f -V echo "second install round" has_rights "${tmpd}/exists" "777" has_rights "${tmpd}/linkchildren/f1" "644" has_rights "${tmpd}/linkchildren/d1" "755" has_rights "${tmpd}/linkchildren/d1/f2" "644" has_rights "${tmpd}/symlinktemplate" "777" m=`get_file_mode` has_rights "${tmpd}/nomode" "${m}" ## no user confirmation expected ## same mode echo "same mode" echo "nomode" > ${tmps}/dotfiles/nomode chmod 600 ${tmps}/dotfiles/nomode echo "nomode" > ${tmpd}/nomode chmod 600 ${tmpd}/nomode cd ${ddpath} | ${bin} install -c ${cfg} -f -p p2 -V f_nomode echo "same mode" has_rights "${tmpd}/nomode" "600" ## no user confirmation with force ## different mode echo "different mode" echo "nomode" > ${tmps}/dotfiles/nomode chmod 600 ${tmps}/dotfiles/nomode echo "nomode" > ${tmpd}/nomode chmod 700 ${tmpd}/nomode cd ${ddpath} | ${bin} install -c ${cfg} -f -p p2 -V f_nomode echo "different mode (1)" has_rights "${tmpd}/nomode" "600" ## user confirmation expected ## different mode echo "different mode" echo "nomode" > ${tmps}/dotfiles/nomode chmod 600 ${tmps}/dotfiles/nomode echo "nomode" > ${tmpd}/nomode chmod 700 ${tmpd}/nomode cd ${ddpath} | printf 'y\ny\n' | ${bin} install -f -c ${cfg} -p p2 -V f_nomode echo "different mode (2)" has_rights "${tmpd}/nomode" "600" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-more.sh000077500000000000000000000053021436430751200174210ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test chmod on import # with files and directories # with different link # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 path # $2 rights has_rights() { echo "testing ${1} is ${2}" [ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1 local mode=`stat -L -c '%a' "$1"` [ "${mode}" != "$2" ] && echo "bad mode for `basename $1` (${mode} instead of ${2})" && exit 1 true } # $1 file chmod_to_umask() { u=`umask` u=`echo ${u} | sed 's/^0*//'` if [ -d ${1} ]; then v=$((777 - u)) else v=$((666 - u)) fi chmod ${v} ${1} } # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfiles f1="${tmpd}/f1" touch ${f1} chmod 777 ${f1} stat -c '%a' ${f1} f2="${tmpd}/f2" touch ${f2} chmod 644 ${f2} stat -c '%a' ${f2} toimport="${f1} ${f2}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import without --preserve-mode for i in ${toimport}; do stat -c '%a' ${i} cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V ${i} done cat ${cfg} has_rights "${tmpd}/f1" "777" has_rights "${tmps}/dotfiles/${tmpd}/f1" "777" has_rights "${tmpd}/f2" "644" has_rights "${tmps}/dotfiles/${tmpd}/f2" "644" # install cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V | grep '0 dotfile(s) installed' || (echo "should not install" && exit 1) echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-preserve-install.sh000077500000000000000000000163611436430751200217650ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test chmod preserve on install # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 path # $2 rights has_rights() { echo "testing ${1} is ${2}" [ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1 local mode=`stat -L -c '%a' "$1"` [ "${mode}" != "$2" ] && echo "bad mode for `basename $1` (${mode} VS expected ${2})" && exit 1 true } # test $1 path has same right than $2 is_same_as() { echo "testing ${1} has same rights than ${2}" [ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1 [ ! -e "$2" ] && echo "`basename $2` does not exist" && exit 1 local mode1=`stat -L -c '%a' "$1"` echo "$1: ${mode1}" local mode2=`stat -L -c '%a' "$2"` echo "$2: ${mode2}" [ "${mode1}" != "${mode2}" ] && echo "`basename $1` (${mode1}) does not have same mode as `basename $2` (${mode2})" && exit 1 true } get_default_file_mode() { u=`umask` u=`echo ${u} | sed 's/^0*//'` v=$((666 - u)) echo "${v}" } get_default_dir_mode() { u=`umask` u=`echo ${u} | sed 's/^0*//'` v=$((777 - u)) echo "${v}" } # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" ## # non existing files ## # file echo 'f777' > ${tmps}/dotfiles/f777 chmod 700 ${tmps}/dotfiles/f777 # link echo 'link' > ${tmps}/dotfiles/link chmod 700 ${tmps}/dotfiles/link # directory mkdir -p ${tmps}/dotfiles/dir echo "f1" > ${tmps}/dotfiles/dir/f1 chmod 700 ${tmps}/dotfiles/dir chmod 700 ${tmps}/dotfiles/dir/f1 # template echo '{{@@ profile @@}}' > ${tmps}/dotfiles/template chmod 700 ${tmps}/dotfiles/template # link template echo '{{@@ profile @@}}' > ${tmps}/dotfiles/link-template chmod 700 ${tmps}/dotfiles/link-template ## # existing files ## # file echo "exists-original" > ${tmps}/dotfiles/exists chmod 644 ${tmps}/dotfiles/exists echo "exists" > ${tmpd}/exists chmod 700 ${tmpd}/exists # link echo "existslink" > ${tmps}/dotfiles/existslink chmod 700 ${tmps}/dotfiles/existslink ln -s ${tmps}/dotfiles/existslink ${tmpd}/existslink # directory mkdir -p ${tmps}/dotfiles/direxists echo "f1-original" > ${tmps}/dotfiles/direxists/f1 mkdir -p ${tmpd}/direxists echo "f1" > ${tmpd}/direxists/f1 chmod 700 ${tmpd}/direxists/f1 chmod 700 ${tmpd}/direxists # link children mkdir -p ${tmps}/dotfiles/linkchildren echo "f1-original" > ${tmps}/dotfiles/linkchildren/f1 chmod 700 ${tmps}/dotfiles/linkchildren/f1 mkdir -p ${tmps}/dotfiles/linkchildren/d1 chmod 700 ${tmps}/dotfiles/linkchildren/d1 echo "f2-original" > ${tmps}/dotfiles/linkchildren/d1/f2 chmod 700 ${tmps}/dotfiles/linkchildren/d1/f2 mkdir -p ${tmpd}/linkchildren chmod 700 ${tmpd}/linkchildren echo "f1" > ${tmpd}/linkchildren/f1 mkdir -p ${tmpd}/linkchildren/d1 echo "f2" > ${tmpd}/linkchildren/d1/f2 # no mode echo 'nomode-original' > ${tmps}/dotfiles/nomode echo 'nomode' > ${tmpd}/nomode cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles force_chmod: true dotfiles: f_f777: src: f777 dst: ${tmpd}/f777 chmod: preserve f_link: src: link dst: ${tmpd}/link chmod: preserve link: absolute d_dir: src: dir dst: ${tmpd}/dir chmod: preserve f_template: src: template dst: ${tmpd}/template chmod: preserve f_link_template: src: link-template dst: ${tmpd}/link-template chmod: preserve f_exists: src: exists dst: ${tmpd}/exists chmod: preserve f_existslink: src: existslink dst: ${tmpd}/existslink chmod: preserve link: absolute d_direxists: src: direxists dst: ${tmpd}/direxists chmod: preserve d_linkchildren: src: linkchildren dst: ${tmpd}/linkchildren chmod: preserve link: link_children f_nomode: src: nomode dst: ${tmpd}/nomode chmod: preserve profiles: p1: dotfiles: - f_f777 - f_link - d_dir - f_template - f_link_template - f_exists - f_existslink - d_direxists - d_linkchildren - f_nomode _EOF #cat ${cfg} exists_before=`stat -L -c '%a' "${tmpd}/exists"` direxists_before=`stat -L -c '%a' "${tmpd}/direxists"` direxists_f1_before=`stat -L -c '%a' "${tmpd}/direxists/f1"` # install echo "first round" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "first round" # non-existing but will create with "default" rights on preserve # 644 for file # 755 for directory # link will get the rights of the file it points to has_rights "${tmpd}/f777" "`get_default_file_mode`" has_rights "${tmpd}/link" "700" has_rights "${tmpd}/dir" "`get_default_dir_mode`" has_rights "${tmpd}/template" "`get_default_file_mode`" # first install to workdir (def rights) and then symlink has_rights "${tmpd}/link-template" "644" [ -L "${tmpd}/link-template" ] && echo "link-template is not a symlink" && exit 1 # existing has_rights "${tmpd}/exists" "700" has_rights "${tmpd}/exists" "${exists_before}" has_rights "${tmpd}/existslink" "700" # points back to dotpath is_same_as "${tmpd}/existslink" "${tmps}/dotfiles/existslink" has_rights "${tmpd}/direxists" "700" has_rights "${tmpd}/direxists" "${direxists_before}" has_rights "${tmpd}/direxists/f1" "700" has_rights "${tmpd}/direxists/f1" "${direxists_f1_before}" has_rights "${tmpd}/linkchildren" "700" # default for new directory has_rights "${tmpd}/linkchildren/f1" "700" # points back to dotpath has_rights "${tmpd}/linkchildren/d1" "700" # points back to dotpath has_rights "${tmpd}/linkchildren/d1/f2" "700" # modify echo 'f777-2' >> ${tmps}/dotfiles/f777 chmod 701 ${tmps}/dotfiles/f777 echo 'link-2' >> ${tmps}/dotfiles/link chmod 701 ${tmps}/dotfiles/link echo "f1-2" >> ${tmps}/dotfiles/dir/f1 chmod 701 ${tmps}/dotfiles/dir chmod 701 ${tmps}/dotfiles/dir/f1 f777_before=`stat -L -c '%a' "${tmpd}/f777"` link_before=`stat -L -c '%a' "${tmpd}/link"` dir_before=`stat -L -c '%a' "${tmpd}/dir"` echo "second round" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "second round" # existing has_rights "${tmpd}/f777" "${f777_before}" has_rights "${tmpd}/link" "${link_before}" has_rights "${tmpd}/dir" "${dir_before}" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-preserve-update.sh000077500000000000000000000074401436430751200215770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test chmod preserve on update # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" ## # existing files ## # file echo "exists-original" > ${tmps}/dotfiles/exists chmod 644 ${tmps}/dotfiles/exists echo "exists" > ${tmpd}/exists chmod 700 ${tmpd}/exists # link echo "existslink" > ${tmps}/dotfiles/existslink chmod 700 ${tmps}/dotfiles/existslink ln -s ${tmps}/dotfiles/existslink ${tmpd}/existslink # directory mkdir -p ${tmps}/dotfiles/direxists echo "f1-original" > ${tmps}/dotfiles/direxists/f1 mkdir -p ${tmpd}/direxists echo "f1" > ${tmpd}/direxists/f1 chmod 700 ${tmpd}/direxists/f1 chmod 700 ${tmpd}/direxists # link children mkdir -p ${tmps}/dotfiles/linkchildren echo "f1-original" > ${tmps}/dotfiles/linkchildren/f1 chmod 700 ${tmps}/dotfiles/linkchildren/f1 mkdir -p ${tmps}/dotfiles/linkchildren/d1 chmod 700 ${tmps}/dotfiles/linkchildren/d1 echo "f2-original" > ${tmps}/dotfiles/linkchildren/d1/f2 chmod 700 ${tmps}/dotfiles/linkchildren/d1/f2 mkdir -p ${tmpd}/linkchildren chmod 700 ${tmpd}/linkchildren echo "f1" > ${tmpd}/linkchildren/f1 mkdir -p ${tmpd}/linkchildren/d1 echo "f2" > ${tmpd}/linkchildren/d1/f2 # no mode echo 'nomode-original' > ${tmps}/dotfiles/nomode echo 'nomode' > ${tmpd}/nomode # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles force_chmod: true dotfiles: f_exists: src: exists dst: ${tmpd}/exists chmod: preserve f_existslink: src: existslink dst: ${tmpd}/existslink chmod: preserve link: absolute d_direxists: src: direxists dst: ${tmpd}/direxists chmod: preserve d_linkchildren: src: linkchildren dst: ${tmpd}/linkchildren chmod: preserve link: link_children f_nomode: src: nomode dst: ${tmpd}/nomode chmod: preserve profiles: p1: dotfiles: - f_exists - f_existslink - d_direxists - d_linkchildren - f_nomode _EOF #cat ${cfg} echo "update" cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/exists cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/existslink cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/direxists cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/linkchildren cd ${ddpath} | ${bin} update -f -c ${cfg} -p p1 -V ${tmpd}/nomode count=$(cat ${cfg} | grep chmod | grep -v 'chmod: preserve\|force_chmod' | wc -l) echo "${count}" [ "${count}" != "0" ] && echo "chmod altered" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/chmod-update.sh000077500000000000000000000104341436430751200177430ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test chmod on update # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile dnormal="${tmpd}/dir_normal" mkdir -p ${dnormal} echo "dir_normal/f1" > ${dnormal}/file1 echo "dir_normal/f2" > ${dnormal}/file2 dlink="${tmpd}/dir_link" mkdir -p ${dlink} echo "dir_link/f1" > ${dlink}/file1 echo "dir_link/f2" > ${dlink}/file2 dlinkchildren="${tmpd}/dir_link_children" mkdir -p ${dlinkchildren} echo "dir_linkchildren/f1" > ${dlinkchildren}/file1 echo "dir_linkchildren/f2" > ${dlinkchildren}/file2 fnormal="${tmpd}/filenormal" echo "filenormal" > ${fnormal} flink="${tmpd}/filelink" echo "filelink" > ${flink} toimport="${dnormal} ${dlink} ${dlinkchildren} ${fnormal} ${flink}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import for i in ${toimport}; do cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V ${i} done cat ${cfg} # test no chmod cnt=`cat ${cfg} | grep chmod | wc -l` [ "${cnt}" != "0" ] && echo "chmod wrongly inserted" && exit 1 ###################### # update dnormal chmod 777 ${dnormal} cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V ${dnormal} # check rights updated [ "`stat -c '%a' ${tmps}/dotfiles/${tmpd}/$(basename ${dnormal})`" != "777" ] && echo "rights not updated (1)" && exit 1 cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "1" ] && echo "chmod not updated (1)" && exit 1 ###################### # update dlink chmod 777 ${dlink} cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V ${dlink} # check rights updated [ "`stat -c '%a' ${tmps}/dotfiles/${tmpd}/$(basename ${dlink})`" != "777" ] && echo "rights not updated (2)" && exit 1 cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "2" ] && echo "chmod not updated (2)" && exit 1 ###################### # update dlinkchildren chmod 777 ${dlinkchildren} cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V ${dlinkchildren} # check rights updated [ "`stat -c '%a' ${tmps}/dotfiles/${tmpd}/$(basename ${dlinkchildren})`" != "777" ] && echo "rights not updated (3)" && exit 1 cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "3" ] && echo "chmod not updated (3)" && exit 1 ###################### # update fnormal chmod 777 ${fnormal} cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V ${fnormal} # check rights updated [ "`stat -c '%a' ${tmps}/dotfiles/${tmpd}/$(basename ${fnormal})`" != "777" ] && echo "rights not updated (4)" && exit 1 cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "4" ] && echo "chmod not updated (4)" && exit 1 ###################### # update flink chmod 777 ${flink} cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V ${flink} # check rights updated [ "`stat -c '%a' ${tmps}/dotfiles/${tmpd}/$(basename ${flink})`" != "777" ] && echo "rights not updated (5)" && exit 1 cnt=`cat ${cfg} | grep "chmod: '777'" | wc -l` [ "${cnt}" != "5" ] && echo "chmod not updated (5)" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/clear-workdir.sh000077500000000000000000000067011436430751200201400ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test clear_workdir # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${basedir}/dotfiles echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` if [ -z "${DOTDROP_WORKDIR}" ]; then tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" clear_on_exit "${tmpw}" fi clear_on_exit "${basedir}" clear_on_exit "${tmpd}" echo "{{@@ profile @@}}" > ${basedir}/dotfiles/x # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: x dst: ${tmpd}/x link: link profiles: p1: dotfiles: - f_x _EOF echo "[+] install (1)" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^1 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/x ] && echo "f_x not installed" && exit 1 [ ! -h ${tmpd}/x ] && echo "f_x not symlink" && exit 1 [ ! -e ${DOTDROP_WORKDIR}/${tmpd}/x ] && echo "f_x not in workdir (${DOTDROP_WORKDIR}/${tmpd})" && exit 1 # add file touch ${DOTDROP_WORKDIR}/new echo "[+] re-install with clear-workdir in cli" cd ${ddpath} | printf "y\n" | ${bin} install -W -c ${cfg} -p p1 --verbose [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/x ] && echo "f_x not installed" && exit 1 [ ! -h ${tmpd}/x ] && echo "f_x not symlink" && exit 1 [ ! -e ${DOTDROP_WORKDIR}/${tmpd}/x ] && echo "f_x not in workdir (${DOTDROP_WORKDIR}/${tmpd})" && exit 1 [ -e ${DOTDROP_WORKDIR}/new ] && echo "workdir not cleared (1)" && exit 1 # add file touch ${DOTDROP_WORKDIR}/new echo "[+] re-install with config clear-workdir in config" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles clear_workdir: true dotfiles: f_x: src: x dst: ${tmpd}/x link: link profiles: p1: dotfiles: - f_x _EOF cd ${ddpath} | printf "y\n" | ${bin} install -W -c ${cfg} -p p1 --verbose [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/x ] && echo "f_x not installed" && exit 1 [ ! -h ${tmpd}/x ] && echo "f_x not symlink" && exit 1 [ ! -e ${DOTDROP_WORKDIR}/${tmpd}/x ] && echo "f_x not in workdir (${DOTDROP_WORKDIR}/${tmpd})" && exit 1 [ -e ${DOTDROP_WORKDIR}/new ] && echo "workdir not cleared (2)" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-ignore-missing.sh000077500000000000000000000060041436430751200217450ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test updates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" dt="${basedir}/dotfiles" mkdir -p ${dt}/folder touch ${dt}/folder/a # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files cp -r ${dt}/folder ${tmpd}/ mkdir -p ${tmpd}/folder touch ${tmpd}/folder/b mkdir ${tmpd}/folder/c # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: thedotfile: dst: ${tmpd}/folder src: folder profiles: p1: dotfiles: - thedotfile _EOF # # Test with no ignore-missing setting # # Expect diff echo "[+] test with no ignore-missing setting" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --profile=p1 [ "$?" = "0" ] && exit 1 set -e # # Test with command-line flga # # Expect no diff echo "[+] test with command-line flag" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --profile=p1 --ignore-missing [ "$?" != "0" ] && exit 1 set -e # # Test with global option # cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles ignore_missing_in_dotdrop: true dotfiles: thedotfile: dst: ${tmpd}/folder src: folder profiles: p1: dotfiles: - thedotfile _EOF # Expect no diff echo "[+] test global option" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --profile=p1 [ "$?" != "0" ] && exit 1 set -e # # Test with dotfile option # cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: thedotfile: dst: ${tmpd}/folder src: folder ignore_missing_in_dotdrop: true profiles: p1: dotfiles: - thedotfile _EOF # Expect no diff echo "[+] test dotfile option" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --profile=p1 [ "$?" != "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-ignore-relative.sh000077500000000000000000000116271436430751200221160ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test compare ignore relative # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/{program,config,vscode} touch ${tmpd}/program/a touch ${tmpd}/config/a touch ${tmpd}/vscode/extensions.txt touch ${tmpd}/vscode/keybindings.json # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f --verbose -c ${cfg} ${tmpd}/program || exit 1 cd ${ddpath} | ${bin} import -f --verbose -c ${cfg} ${tmpd}/config || exit 1 cd ${ddpath} | ${bin} import -f --verbose -c ${cfg} ${tmpd}/vscode || exit 1 # add files on filesystem echo "[+] add files" touch ${tmpd}/program/b touch ${tmpd}/config/b # expects diff echo "[+] comparing normal - diffs expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose ret="$?" echo ${ret} [ "${ret}" = "0" ] && exit 1 set -e # expects one diff patt="b" echo "[+] comparing with ignore (pattern: ${patt}) - no diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt} [ "$?" != "0" ] && exit 1 set -e # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "b"' ${cfg} > ${cfg2} #cat ${cfg2} # expects one diff echo "[+] comparing with ignore in dotfile - diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" = "0" ] && exit 1 set -e # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "b"' ${cfg} > ${cfg2} sed -i '/d_program:/a \ \ \ \ cmpignore:\n\ \ \ \ - "b"' ${cfg2} #cat ${cfg2} # expects no diff echo "[+] comparing with ignore in dotfile - no diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" != "0" ] && exit 1 set -e # update files echo touched > ${tmpd}/vscode/extensions.txt echo touched > ${tmpd}/vscode/keybindings.json # expect two diffs echo "[+] comparing - diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/vscode [ "$?" = "0" ] && exit 1 set -e # expects no diff echo "[+] comparing with ignore in dotfile - no diff expected" sed '/d_vscode:/a \ \ \ \ cmpignore:\n\ \ \ \ - "extensions.txt"\n\ \ \ \ - "keybindings.json"' ${cfg} > ${cfg2} set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose -C ${tmpd}/vscode [ "$?" != "0" ] && exit 1 set -e #################### # test for #149 #################### mkdir -p ${tmpd}/.zsh touch ${tmpd}/.zsh/somefile mkdir -p ${tmpd}/.zsh/plugins touch ${tmpd}/.zsh/plugins/someplugin echo "[+] import .zsh" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/.zsh # no diff expected echo "[+] comparing .zsh" cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/.zsh --ignore=${patt} [ "$?" != "0" ] && exit 1 # add some files touch ${tmpd}/.zsh/plugins/ignore-1.zsh touch ${tmpd}/.zsh/plugins/ignore-2.zsh # expects diff echo "[+] comparing .zsh with new files" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/.zsh ret="$?" echo ${ret} [ "${ret}" = "0" ] && exit 1 set -e # expects no diff patt="plugins/ignore-*.zsh" echo "[+] comparing with ignore (pattern: ${patt}) - no diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/.zsh --ignore=${patt} [ "$?" != "0" ] && exit 1 set -e # expects no diff echo "[+] comparing with ignore in dotfile - no diff expected" sed '/d_zsh:/a \ \ \ \ cmpignore:\n\ \ \ \ - "plugins/ignore-*.zsh"' ${cfg} > ${cfg2} set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose -C ${tmpd}/.zsh [ "$?" != "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-ignore.sh000077500000000000000000000117651436430751200203100ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test updates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/{program,config} touch ${tmpd}/program/a touch ${tmpd}/config/a mkdir ${tmpd}/vscode touch ${tmpd}/vscode/extensions.txt touch ${tmpd}/vscode/keybindings.json # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/config cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/vscode # add files echo "[+] add files" touch ${tmpd}/program/b touch ${tmpd}/config/b # expects diff echo "[+] comparing normal - 2 diffs" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose [ "$?" = "0" ] && exit 1 set -e # expects one diff patt="${tmpd}/config/b" echo "[+] comparing with ignore (pattern: ${patt}) - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt} [ "$?" = "0" ] && exit 1 set -e # expects no diff patt="*b" echo "[+] comparing with ignore (pattern: ${patt}) - 0 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt} [ "$?" != "0" ] && exit 1 set -e # expects one diff patt="*/config/*b" echo "[+] comparing with ignore (pattern: ${patt}) - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt} [ "$?" = "0" ] && exit 1 set -e #cat ${cfg} # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*/config/b"' ${cfg} > ${cfg2} #cat ${cfg2} # expects one diff echo "[+] comparing with ignore in dotfile - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" = "0" ] && exit 1 set -e # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_config:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*b"' ${cfg} > ${cfg2} sed -i '/d_program:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*b"' ${cfg2} #cat ${cfg2} # expects no diff patt="*b" echo "[+] comparing with ignore in dotfile - 0 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" != "0" ] && exit 1 set -e # update files echo touched > ${tmpd}/vscode/extensions.txt echo touched > ${tmpd}/vscode/keybindings.json # expect two diffs set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/vscode [ "$?" = "0" ] && exit 1 set -e # expects no diff sed '/d_vscode:/a \ \ \ \ cmpignore:\n\ \ \ \ - "*extensions.txt"\n\ \ \ \ - "*keybindings.json"' ${cfg} > ${cfg2} set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose -C ${tmpd}/vscode [ "$?" != "0" ] && exit 1 set -e # clean rm -rf ${basedir}/dotfiles mkdir -p ${basedir}/dotfiles # create dotfiles/dirs mkdir -p ${tmpd}/{program,config,vscode} touch ${tmpd}/program/a touch ${tmpd}/config/a touch ${tmpd}/vscode/extensions.txt touch ${tmpd}/vscode/keybindings.json touch ${tmpd}/vscode/keybindings.json # create the config file cfg="${basedir}/config3.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program ${tmpd}/config ${tmpd}/vscode # create the files to ignore touch ${tmpd}/program/.DS_Store touch ${tmpd}/config/.DS_Store touch ${tmpd}/vscode/.DS_Store # ensure not imported found=`find ${basedir}/dotfiles/ -iname '.DS_Store'` [ "${found}" != "" ] && echo "imported ???" && exit 1 # general ignore echo "[+] comparing ..." cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -i '*/.DS_Store' [ "$?" != "0" ] && exit 1 # general ignore echo "[+] comparing2 ..." sed '/^config:$/a\ \ cmpignore:\n\ \ - "*/.DS_Store"' ${cfg} > ${cfg2} cat ${cfg2} cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" != "0" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-include.sh000077500000000000000000000066201436430751200204420ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2023, deadc0de6 # # test compare in profile which includes another # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfiles already imported echo "already in" > ${tmps}/dotfiles/abc cp ${tmps}/dotfiles/abc ${tmpd}/abc # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: dotfiles: include: - p1 - p2 p1: dotfiles: variables: somevar: somevalue p2: dotfiles: - f_abc _EOF cat ${cfg} cd ${ddpath} | ${bin} files -c ${cfg} -p p0 cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep '^f_' | wc -l` [ "${cnt}" != "1" ] && echo "this is bad" && exit 1 # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p0 echo "modifying" echo 'modified' > ${tmpd}/abc # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p0 ret=$? [ "${ret}" = "0" ] && echo "compare should fail (returned ${ret})" && exit 1 set -e # count cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 -b | grep '^f_' | wc -l` [ "${cnt}" != "2" ] && echo "not enough dotfile" exit 1 ## without dotfiles: entry # reset dotfile content echo "already in" > ${tmps}/dotfiles/abc cp ${tmps}/dotfiles/abc ${tmpd}/abc cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: include: - p1 - p2 p1: variables: somevar: somevalue p2: dotfiles: - f_abc _EOF cat ${cfg} cd ${ddpath} | ${bin} files -c ${cfg} -p p0 cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep '^f_' | wc -l` [ "${cnt}" != "1" ] && echo "this is bad" && exit 1 # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p0 echo "modifying" echo 'modified' > ${tmpd}/abc # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p0 ret=$? [ "${ret}" = "0" ] && echo "compare should fail (returned ${ret})" && exit 1 set -e # count cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 -b | grep '^f_' | wc -l` [ "${cnt}" != "2" ] && echo "not enough dotfile" exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-negative-ignore-relative.sh000077500000000000000000000106371436430751200237160ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test compare negative ignore relative # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/program/ignore_me echo "some data" > ${tmpd}/program/a echo "some data" > ${tmpd}/program/ignore_me/b echo "some data" > ${tmpd}/program/ignore_me/c # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program # make some changes to generate a diff echo "some other data" > ${tmpd}/program/a echo "some other data" > ${tmpd}/program/ignore_me/b echo "some other data" > ${tmpd}/program/ignore_me/c # expects two diffs (no need to test comparing normal - 3 diffs, as that is taken care of in compare-negative-ignore.sh) patt0="ignore_me/*" patt1="!ignore_me/c" echo "[+] comparing with ignore (patterns: ${patt0} and ${patt1}) - 2 diffs" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt0} --ignore=${patt1} [ "$?" = "0" ] && exit 1 set -e ######################################## # Test ignores specified in config.yaml ######################################## # add some files mkdir -p ${tmpd}/.zsh echo "some data" > ${tmpd}/.zsh/somefile mkdir -p ${tmpd}/.zsh/plugins echo "some data" > ${tmpd}/.zsh/plugins/someplugin echo "[+] import .zsh" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/.zsh touch ${tmpd}/.zsh/plugins/ignore-1.zsh touch ${tmpd}/.zsh/plugins/ignore-2.zsh # adding ignore in config.yaml cfg2="${basedir}/config2.yaml" sed '/d_zsh:/a\ \ \ \ \ cmpignore:\ \ \ \ \ - "plugins/ignore-?.zsh"\ \ \ \ \ - "!plugins/ignore-2.zsh" ' ${cfg} > ${cfg2} # expects one diff patt0="plugins/ignore-?.zsh" patt1="!plugins/ignore-2.zsh" echo "[+] comparing with ignore (patterns: ${patt0} and ${patt1}) - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -C ${tmpd}/.zsh --ignore=${patt0} --ignore=${patt1} [ "$?" = "0" ] && exit 1 set -e # expects one diff echo "[+] comparing .zsh with ignore in dotfile - 1 diff expected" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose -C ${tmpd}/.zsh ret="$?" echo ${ret} [ "${ret}" = "0" ] && exit 1 set -e echo "OK" dotdrop-1.12.9/tests-ng/compare-negative-ignore.sh000077500000000000000000000071611436430751200221030ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test install negative ignore absolute/relative # returns 1 in case of error # # exit on first error #set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/program/ignore_me echo "some data" > ${tmpd}/program/a echo "some data" > ${tmpd}/program/ignore_me/b echo "some data" > ${tmpd}/program/ignore_me/c # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program # make some changes to generate a diff echo "some other data" > ${tmpd}/program/a echo "some other data" > ${tmpd}/program/ignore_me/b echo "some other data" > ${tmpd}/program/ignore_me/c echo "[+] comparing normal - 3 diffs" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose [ "$?" = 0 ] && exit 1 # We don't want an exit status of 0 set -e # expects two diffs patt0="*/ignore_me/*" patt1="!*/ignore_me/c" echo "[+] comparing with ignore (patterns: ${patt0} and ${patt1}) - 2 diffs" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose --ignore=${patt0} --ignore=${patt1} [ "$?" = "0" ] && exit 1 set -e # Adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_program:/a\ \ \ \ \ cmpignore:\ \ \ \ \ - "*/ignore_me/*"\ \ \ \ \ - "!*/ignore_me/c" ' ${cfg} > ${cfg2} # still expects two diffs echo "[+] comparing with ignore in dotfile - 2 diffs" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" = "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare-profile-check.sh000077500000000000000000000063341436430751200215340ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test compare dotfile from different profile # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests-source' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt} xori="profile x" xori="profile y" echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests-dest' || mktemp -d` touch ${tmpd}/file clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_file_x: dst: ${tmpd}/file src: file_x f_file_y: dst: ${tmpd}/file src: file_y profiles: x: dotfiles: - f_file_x y: dotfiles: - f_file_y _EOF cat ${cfg} # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y echo "test compare profile x (ok)" echo "${xori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p x -C ${tmpd}/file [ "$?" != "0" ] && exit 1 set -e echo "test compare profile x (not ok)" echo "${yori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p x -C ${tmpd}/file [ "$?" = "0" ] && exit 1 set -e echo "test compare profile y (ok)" echo "${yori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p y -C ${tmpd}/file [ "$?" != "0" ] && exit 1 set -e echo "test compare profile y (not ok)" echo "${xori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p y -C ${tmpd}/file [ "$?" = "0" ] && exit 1 set -e echo "test compare profile x generic (ok)" echo "${xori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p x [ "$?" != "0" ] && exit 1 set -e echo "test compare profile x generic (not ok)" echo "${yori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p x [ "$?" = "0" ] && exit 1 set -e echo "test compare profile y generic (ok)" echo "${yori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p y [ "$?" != "0" ] && exit 1 set -e echo "test compare profile y generic (not ok)" echo "${xori}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose -p y [ "$?" = "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/compare.sh000077500000000000000000000063321436430751200170210ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test updates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # single file echo 'unique' > ${tmpd}/uniquefile # hierarchy from https://pymotw.com/2/filecmp/ # create the hierarchy # for dir1 (originally imported directory)))) mkdir ${tmpd}/dir1 touch ${tmpd}/dir1/file_only_in_dir1 mkdir -p ${tmpd}/dir1/dir_only_in_dir1 mkdir -p ${tmpd}/dir1/common_dir echo 'this file is the same' > ${tmpd}/dir1/common_file echo 'in dir1' > ${tmpd}/dir1/not_the_same echo 'This is a file in dir1' > ${tmpd}/dir1/file_in_dir1 mkdir -p ${tmpd}/dir1/sub/sub2 mkdir -p ${tmpd}/dir1/notindir2/notindir2 echo 'first' > ${tmpd}/dir1/sub/sub2/different #tree ${tmpd}/dir1 # create the hierarchy # for dir2 (modified original for update) mkdir ${tmpd}/dir2 touch ${tmpd}/dir2/file_only_in_dir2 mkdir -p ${tmpd}/dir2/dir_only_in_dir2 mkdir -p ${tmpd}/dir2/common_dir echo 'this file is the same' > ${tmpd}/dir2/common_file echo 'in dir2' > ${tmpd}/dir2/not_the_same mkdir -p ${tmpd}/dir2/file_in_dir1 mkdir -p ${tmpd}/dir2/sub/sub2 echo 'modified' > ${tmpd}/dir2/sub/sub2/different mkdir -p ${tmpd}/dir2/new/new2 #tree ${tmpd}/dir2 # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import dir1 echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/dir1 cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/uniquefile cat ${cfg} # let's see the dotpath #tree ${basedir}/dotfiles # change dir1 to dir2 in deployed echo "[+] change dir" rm -rf ${tmpd}/dir1 mv ${tmpd}/dir2 ${tmpd}/dir1 #tree ${tmpd}/dir1 # change unique file echo 'changed' > ${tmpd}/uniquefile # compare echo "[+] comparing" set +e cd ${ddpath} | ${bin} compare -c ${cfg} --verbose [ "$?" = "0" ] && exit 1 set -e echo "[+] comparing with file-only" set +e cd ${ddpath} | ${bin} compare -c ${cfg} -L [ "$?" = "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/corner-case.sh000077500000000000000000000050371436430751200175750ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # the only purpose is to test corner-cases # not covered by other tests like # dry # diff before write # etc # # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" clear_on_exit "${basedir}" export DOTDROP_WORKERS=1 # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: /tmp/x dst: f_y: src: /tmp/.i-do-not-exist-dotdrop dst: /tmp/y profiles: p1: dotfiles: - f_x - f_y _EOF echo "[+] test install dry" cd ${ddpath} | ${bin} install -c ${cfg} --dry -p p1 --verbose f_x [ "$?" != "0" ] && exit 1 echo "[+] test install show-diff" cd ${ddpath} | ${bin} install -c ${cfg} -p p1 --verbose f_x [ "$?" != "0" ] && exit 1 cd ${ddpath} | ${bin} install -D -c ${cfg} -p p1 --verbose f_x [ "$?" != "0" ] && exit 1 echo "[+] test install not existing src" cd ${ddpath} | ${bin} install -c ${cfg} -f --dry -p p1 --verbose f_y echo "[+] test install to temp" cd ${ddpath} | ${bin} install -t -c ${cfg} -p p1 --verbose f_x > ${basedir}/log 2>&1 [ "$?" != "0" ] && echo "install to tmp failed" && exit 1 # cleaning tmpfile=`cat ${basedir}/log | grep 'installed to tmp ' | sed 's/^.*to tmp "\(.*\)"./\1/'` echo "tmpfile: ${tmpfile}" rm -rf "${tmpfile}" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/deprecated-link.sh000077500000000000000000000070251436430751200204260ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test migration from link/link_children to single entry # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles link_by_default: true dotfiles: f_link: dst: ${tmpd}/abc src: abc link: true f_link1: dst: ${tmpd}/abc src: abc link: true f_nolink: dst: ${tmpd}/abc src: abc link: false f_nolink1: dst: ${tmpd}/abc src: abc link: false f_children: dst: ${tmpd}/abc src: abc link_children: true f_children2: dst: ${tmpd}/abc src: abc link_children: true f_children3: dst: ${tmpd}/abc src: abc link_children: false profiles: p1: dotfiles: - f_link - f_nolink - f_nolink1 - f_children - f_children2 - f_children3 _EOF cat ${cfg} # create the dotfiles echo "test" > ${tmps}/dotfiles/abc echo "test" > ${tmpd}/abc # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 # install #cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V cat ${cfg} # fail if find some of these entries echo "========> test for bad entries" set +e grep 'link_children: true' ${cfg} >/dev/null && exit 1 grep 'link_children: false' ${cfg} >/dev/null && exit 1 grep 'link: true' ${cfg} >/dev/null && exit 1 grep 'link: false' ${cfg} >/dev/null && exit 1 grep 'link_by_default: true' ${cfg} >/dev/null && exit 1 grep 'link_by_default: false' ${cfg} >/dev/null && exit 1 set -e # test values have been correctly updated echo "========> test for updated entries" cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_link' | head -1 | grep ',link:absolute,' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_nolink' | head -1 | grep ',link:nolink,' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_nolink1' | head -1 | grep ',link:nolink,' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_children' | head -1 | grep ',link:link_children,' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_children2' | head -1 | grep ',link:link_children,' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | grep '^f_children3' | head -1 | grep ',link:nolink,' echo "OK" exit 0 dotdrop-1.12.9/tests-ng/diff-cmd.sh000077500000000000000000000062701436430751200170450ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test diff cmd # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files echo "original" > ${tmpd}/singlefile # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/singlefile # modify the file echo "modified" > ${tmpd}/singlefile # default diff (unified) echo "[+] comparing with default diff (unified)" set +e cd ${ddpath} | ${bin} compare -b -c ${cfg} 2>/dev/null | grep -v '=>' | grep -v '\->' | grep -v 'dotfile(s) compared' | sed '$d' | grep -v '^+++\|^---' > ${tmpd}/normal diff -u -r ${tmpd}/singlefile ${basedir}/dotfiles/${tmpd}/singlefile | grep -v '^+++\|^---' > ${tmpd}/real set -e # verify diff ${tmpd}/normal ${tmpd}/real || exit 1 # adding normal diff cfg2="${basedir}/config2.yaml" sed '/dotpath: dotfiles/a \ \ diff_command: "diff -r {0} {1}"' ${cfg} > ${cfg2} #cat ${cfg2} # normal diff echo "[+] comparing with normal diff" set +e cd ${ddpath} | ${bin} compare -b -c ${cfg2} 2>/dev/null | grep -v '=>' | grep -v '\->' | grep -v 'dotfile(s) compared' | sed '$d' > ${tmpd}/unified diff -r ${tmpd}/singlefile ${basedir}/dotfiles/${tmpd}/singlefile > ${tmpd}/real set -e # verify #cat ${tmpd}/unified #cat ${tmpd}/real diff ${tmpd}/unified ${tmpd}/real || exit 1 # adding fake diff cfg3="${basedir}/config3.yaml" sed '/dotpath: dotfiles/a \ \ diff_command: "echo fakediff"' ${cfg} > ${cfg3} #cat ${cfg3} # fake diff echo "[+] comparing with fake diff" set +e cd ${ddpath} | ${bin} compare -b -c ${cfg3} 2>/dev/null | grep -v '=>' | grep -v '\->' | grep -v 'dotfile(s) compared' | sed '$d' > ${tmpd}/fake set -e # verify #cat ${tmpd}/fake grep fakediff ${tmpd}/fake &> /dev/null || exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dir-import-update.sh000077500000000000000000000034671436430751200207470ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test importing and updating entire directories # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` dotfiles="${basedir}/dotfiles" echo "dotdrop dir: ${basedir}" # the dotfile tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` create_dir ${tmpd} clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import the dir cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd} # change token echo "changed" > ${token} # update cd ${ddpath} | ${bin} update -f -c ${cfg} ${tmpd} --verbose grep 'changed' ${token} >/dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotdrop-variables.sh000077500000000000000000000043531436430751200210150ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test dotdrop auto-added variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: /tmp/xxx dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "dotpath: {{@@ _dotdrop_dotpath @@}}" > ${tmps}/dotfiles/abc echo "cfgpath: {{@@ _dotdrop_cfgpath @@}}" >> ${tmps}/dotfiles/abc echo "workdir: {{@@ _dotdrop_workdir @@}}" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V cat ${tmpd}/abc grep "^dotpath: ${tmps}/dotfiles$" ${tmpd}/abc >/dev/null grep "^cfgpath: ${tmps}/config.yaml$" ${tmpd}/abc >/dev/null grep "^workdir: /tmp/xxx$" ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotfile-no-src.sh000077500000000000000000000040431436430751200202150ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test dotfiles with no 'src' # returns 1 in case of error # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: abc: dst: ${tmpd}/abc profiles: p1: dotfiles: - ALL _EOF #cat ${cfg} # create the dotfiles echo "abc" > ${tmps}/dotfiles/abc ########################### # test install and compare ########################### # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V [ "$?" != "0" ] && exit 1 # checks [ ! -e ${tmpd}/abc ] && exit 1 grep 'abc' ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotfile-sub-variables.sh000077500000000000000000000053131436430751200215540ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dotfile sub file specific variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: d_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - d_abc _EOF #cat ${cfg} # create the dotfile mkdir -p ${tmps}/dotfiles/abc # file1 echo 'src:{{@@ _dotfile_sub_abs_src @@}}' > ${tmps}/dotfiles/abc/file1 echo 'dst:{{@@ _dotfile_sub_abs_dst @@}}' >> ${tmps}/dotfiles/abc/file1 # file2 mkdir -p ${tmps}/dotfiles/abc/subdir echo 'src:{{@@ _dotfile_sub_abs_src @@}}' > ${tmps}/dotfiles/abc/subdir/file2 echo 'dst:{{@@ _dotfile_sub_abs_dst @@}}' >> ${tmps}/dotfiles/abc/subdir/file2 # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ ! -d ${tmpd}/abc ] && echo 'dotfile not installed' && exit 1 [ ! -e ${tmpd}/abc/file1 ] && echo 'dotfile sub src not installed' && exit 1 [ ! -e ${tmpd}/abc/subdir/file2 ] && echo 'dotfile sub dst not installed' && exit 1 cat ${tmpd}/abc/file1 cat ${tmpd}/abc/subdir/file2 grep "src:${tmps}/dotfiles/abc/file1" ${tmpd}/abc/file1 >/dev/null grep "dst:${tmpd}/abc/file1" ${tmpd}/abc/file1>/dev/null grep "src:${tmps}/dotfiles/abc/subdir/file2" ${tmpd}/abc/subdir/file2 >/dev/null grep "dst:${tmpd}/abc/subdir/file2" ${tmpd}/abc/subdir/file2 >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotfile-variables.sh000077500000000000000000000044161436430751200207700ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dotfile specific variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo 'src:{{@@ _dotfile_abs_src @@}}' > ${tmps}/dotfiles/abc echo 'dst:{{@@ _dotfile_abs_dst @@}}' >> ${tmps}/dotfiles/abc echo 'key:{{@@ _dotfile_key @@}}' >> ${tmps}/dotfiles/abc echo 'link:{{@@ _dotfile_link @@}}' >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ ! -e ${tmpd}/abc ] && echo 'dotfile not installed' && exit 1 cat ${tmpd}/abc grep "src:${tmps}/dotfiles/abc" ${tmpd}/abc >/dev/null grep "dst:${tmpd}/abc" ${tmpd}/abc >/dev/null grep "key:f_abc" ${tmpd}/abc >/dev/null grep "link:nolink" ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotfiles-all.sh000077500000000000000000000047621436430751200177570ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test ALL dotfiles # returns 1 in case of error # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def d_ghi: dst: ${tmpd}/ghi src: ghi profiles: p1: dotfiles: - ALL _EOF #cat ${cfg} # create the dotfiles echo "abc" > ${tmps}/dotfiles/abc echo "def" > ${tmps}/dotfiles/def echo "ghi" > ${tmps}/dotfiles/ghi ########################### # test install and compare ########################### # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V [ "$?" != "0" ] && exit 1 # checks [ ! -e ${tmpd}/abc ] && exit 1 [ ! -e ${tmpd}/def ] && exit 1 [ ! -e ${tmpd}/ghi ] && exit 1 # modify the dotfiles echo "abc-modified" > ${tmps}/dotfiles/abc echo "def-modified" > ${tmps}/dotfiles/def echo "ghi-modified" > ${tmps}/dotfiles/ghi # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V ret="$?" set -e [ "$ret" = "0" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dotfiles-dyn-paths.sh000077500000000000000000000041541436430751200211110ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test dotfile dynamic paths # returns 1 in case of error # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: dst: "${tmpd}/abc" dynvariables: src: "echo abc" dotfiles: f_abc: dst: "{{@@ dst @@}}" src: "{{@@ src @@}}" profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfiles echo "abc" > ${tmps}/dotfiles/abc ########################### # test install and compare ########################### # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V [ "$?" != "0" ] && exit 1 # checks [ ! -e ${tmpd}/abc ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/double-config-import.sh000077500000000000000000000045601436430751200214210ustar00rootroot00000000000000#!/usr/bin/env bash # author: davla (https://github.com/davla) # Copyright (c) 2022, deadc0de6 # # test error report on importing the same sub-config file more than once # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotfile source path src="$(mktemp -d --suffix='-dotdrop-tests' || mktemp -d)" mkdir -p "${src}/dotfiles" clear_on_exit "${src}" # dotfile destination dst="$(mktemp -d --suffix='-dotdrop-tests' || mktemp -d)" clear_on_exit "${dst}" error_log="${dst}/error.log" # bottom-level bottom_level_cfg="${src}/bottom-level.yaml" cat > ${bottom_level_cfg} << _EOF config: backup: true create: true dotpath: ${src}/dotfiles dotfiles: [] profiles: [] _EOF touch "${src}/dotfiles/bottom" # mid-level mid_level_cfg="${src}/mid-level.yaml" cat > ${mid_level_cfg} << _EOF config: backup: true create: true dotpath: ${src}/dotfiles import_configs: - ${bottom_level_cfg} dotfiles: [] profiles: [] _EOF # top-level top_level_cfg="${src}/top-level.yaml" cat > ${top_level_cfg} << _EOF config: backup: true create: true dotpath: ${src}/dotfiles import_configs: - ${mid_level_cfg} - ${bottom_level_cfg} dotfiles: [] profiles: [] _EOF # install set +e cd ${ddpath} | ${bin} install -f -c ${top_level_cfg} -p top-level 2> "${error_log}" set -e # checks grep "${bottom_level_cfg} imported more than once in ${top_level_cfg}" "${error_log}" > /dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dry.sh000077500000000000000000000174441436430751200161770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test dry # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" # workdir tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" # temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" clear_on_exit "${tmpa}" # ----------------------------- # test install # ----------------------------- # cleaning rm -rf ${tmps}/* mkdir -p ${tmps}/dotfiles rm -rf ${tmpw}/* rm -rf ${tmpd}/* rm -rf ${tmpa}/* # create the config file cfg="${tmps}/config.yaml" echo '{{@@ profile @@}}' > ${tmps}/dotfiles/file echo '{{@@ profile @@}}' > ${tmps}/dotfiles/link mkdir -p ${tmps}/dotfiles/dir echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir/f1 mkdir -p ${tmps}/dotfiles/dirchildren echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dirchildren/f1 echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dirchildren/f2 cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} actions: pre: preaction: echo 'pre' > ${tmpa}/pre post: postaction: echo 'post' > ${tmpa}/post dotfiles: f_file: src: file dst: ${tmpd}/file actions: - preaction - postaction f_link: src: link dst: ${tmpd}/link link: link actions: - preaction - postaction d_dir: src: dir dst: ${tmpd}/dir actions: - preaction - postaction d_dirchildren: src: dirchildren dst: ${tmpd}/dirchildren link: link_children actions: - preaction - postaction profiles: p1: dotfiles: - f_file - f_link - d_dir - d_dirchildren _EOF # install echo "dry install" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V --dry cnt=`ls -1 ${tmpd} | wc -l` ls -1 ${tmpd} [ "${cnt}" != "0" ] && echo "dry install failed (1)" && exit 1 cnt=`ls -1 ${tmpw} | wc -l` ls -1 ${tmpw} [ "${cnt}" != "0" ] && echo "dry install failed (2)" && exit 1 cnt=`ls -1 ${tmpa} | wc -l` ls -1 ${tmpa} [ "${cnt}" != "0" ] && echo "dry install failed (3)" && exit 1 # ----------------------------- # test import # ----------------------------- # cleaning rm -rf ${tmps}/* mkdir -p ${tmps}/dotfiles rm -rf ${tmpw}/* rm -rf ${tmpd}/* rm -rf ${tmpa}/* # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: profiles: _EOF cp ${cfg} ${tmpa}/config.yaml echo 'content' > ${tmpd}/file echo 'content' > ${tmpd}/link mkdir -p ${tmpd}/dir echo "content" > ${tmpd}/dir/f1 mkdir -p ${tmpd}/dirchildren echo "content" > ${tmpd}/dirchildren/f1 echo "content" > ${tmpd}/dirchildren/f2 dotfiles="${tmpd}/file ${tmpd}/link ${tmpd}/dir ${tmpd}/dirchildren" echo "dry import" cd ${ddpath} | ${bin} import -c ${cfg} -f -p p1 -V --dry ${dotfiles} cnt=`ls -1 ${tmps}/dotfiles | wc -l` ls -1 ${tmps}/dotfiles [ "${cnt}" != "0" ] && echo "dry import failed (1)" && exit 1 diff ${cfg} ${tmpa}/config.yaml || (echo "dry import failed (2)" && exit 1) # ----------------------------- # test update # ----------------------------- # cleaning rm -rf ${tmps}/* mkdir -p ${tmps}/dotfiles rm -rf ${tmpw}/* rm -rf ${tmpd}/* rm -rf ${tmpa}/* echo 'original' > ${tmps}/dotfiles/file echo 'original' > ${tmps}/dotfiles/link mkdir -p ${tmps}/dotfiles/dir echo "original" > ${tmps}/dotfiles/dir/f1 mkdir -p ${tmps}/dotfiles/dirchildren echo "original" > ${tmps}/dotfiles/dirchildren/f1 echo "original" > ${tmps}/dotfiles/dirchildren/f2 echo 'modified' > ${tmpd}/file echo 'modified' > ${tmpd}/link mkdir -p ${tmpd}/dir echo "modified" > ${tmpd}/dir/f1 mkdir -p ${tmpd}/dirchildren echo "modified" > ${tmpd}/dirchildren/f1 echo "modified" > ${tmpd}/dirchildren/f2 cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_file: src: file dst: ${tmpd}/file f_link: src: link dst: ${tmpd}/link link: link d_dir: src: dir dst: ${tmpd}/dir d_dirchildren: src: dirchildren dst: ${tmpd}/dirchildren link: link_children profiles: p1: dotfiles: - f_file - f_link - d_dir - d_dirchildren _EOF cp ${cfg} ${tmpa}/config.yaml echo "dry update" dotfiles="${tmpd}/file ${tmpd}/link ${tmpd}/dir ${tmpd}/dirchildren" cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 -V --dry ${dotfiles} grep 'modified' ${tmps}/dotfiles/file && echo "dry update failed (1)" && exit 1 grep 'modified' ${tmps}/dotfiles/link && echo "dry update failed (2)" && exit 1 grep "modified" ${tmps}/dotfiles/dir/f1 && echo "dry update failed (3)" && exit 1 grep "modified" ${tmps}/dotfiles/dirchildren/f1 && echo "dry update failed (4)" && exit 1 grep "modified" ${tmps}/dotfiles/dirchildren/f2 && echo "dry update failed (5)" && exit 1 diff ${cfg} ${tmpa}/config.yaml || (echo "dry update failed (6)" && exit 1) # ----------------------------- # test remove # ----------------------------- # cleaning rm -rf ${tmps}/* mkdir -p ${tmps}/dotfiles rm -rf ${tmpw}/* rm -rf ${tmpd}/* rm -rf ${tmpa}/* echo '{{@@ profile @@}}' > ${tmps}/dotfiles/file echo '{{@@ profile @@}}' > ${tmps}/dotfiles/link mkdir -p ${tmps}/dotfiles/dir echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir/f1 mkdir -p ${tmps}/dotfiles/dirchildren echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dirchildren/f1 echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dirchildren/f2 cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_file: src: file dst: ${tmpd}/file f_link: src: link dst: ${tmpd}/link link: link d_dir: src: dir dst: ${tmpd}/dir d_dirchildren: src: dirchildren dst: ${tmpd}/dirchildren link: link_children profiles: p1: dotfiles: - f_file - f_link - d_dir - d_dirchildren _EOF cp ${cfg} ${tmpa}/config.yaml echo "dry remove" dotfiles="${tmpd}/file ${tmpd}/link ${tmpd}/dir ${tmpd}/dirchildren" cd ${ddpath} | ${bin} remove -c ${cfg} -f -p p1 -V --dry ${dotfiles} [ ! -e ${tmps}/dotfiles/file ] && echo "dry remove failed (1)" && exit 1 [ ! -e ${tmps}/dotfiles/link ] && echo "dry remove failed (2)" && exit 1 [ ! -d ${tmps}/dotfiles/dir ] && echo "dry remove failed (3)" && exit 1 [ ! -e ${tmps}/dotfiles/dir/f1 ] && echo "dry remove failed (4)" && exit 1 [ ! -d ${tmps}/dotfiles/dirchildren ] && echo "dry remove failed (5)" && exit 1 [ ! -e ${tmps}/dotfiles/dirchildren/f1 ] && echo "dry remove failed (6)" && exit 1 [ ! -e ${tmps}/dotfiles/dirchildren/f2 ] && echo "dry remove failed (7)" && exit 1 diff ${cfg} ${tmpa}/config.yaml || (echo "dry remove failed (8)" && exit 1) echo "OK" exit 0 dotdrop-1.12.9/tests-ng/duplicate-key.sh000077500000000000000000000045331436430751200201340ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test duplicate keys # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the imported one mkdir -p ${tmps}/dotfiles/${tmpd} echo "test" > ${tmps}/dotfiles/${tmpd}/abc echo "test" > ${tmpd}/abc # create the to-be-imported mkdir -p ${tmpd}/sub echo "test2" > ${tmpd}/sub/abc mkdir -p ${tmpd}/sub/sub2 echo "test2" > ${tmpd}/sub/sub2/abc mkdir -p ${tmpd}/sub/sub echo "test2" > ${tmpd}/sub/sub/abc # import cd ${ddpath} | ${bin} import -f --verbose -c ${cfg} -p p2 \ ${tmpd}/abc \ ${tmpd}/sub/abc \ ${tmpd}/sub/abc \ ${tmpd}/sub/sub/abc \ ${tmpd}/sub/sub2/abc # count dotfiles for p2 cnt=`cd ${ddpath} | ${bin} files --verbose -c ${cfg} -p p2 -b | grep '^f_' | wc -l` [ "${cnt}" != "4" ] && echo "bad count for p2: ${cnt} != 4" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dynactions.sh000077500000000000000000000062151436430751200175460ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dynamic actions # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF variables: var1: "var1" var2: "{{@@ var1 @@}} var2" var3: "{{@@ var2 @@}} var3" var4: "{{@@ dvar4 @@}}" dynvariables: dvar1: "echo dvar1" dvar2: "{{@@ dvar1 @@}} dvar2" dvar3: "{{@@ dvar2 @@}} dvar3" dvar4: "echo {{@@ var3 @@}}" actions: pre: preaction1: "echo {{@@ var3 @@}} > ${tmpa}/preaction1" preaction2: "echo {{@@ dvar3 @@}} > ${tmpa}/preaction2" post: postaction1: "echo {{@@ var3 @@}} > ${tmpa}/postaction1" postaction2: "echo {{@@ dvar3 @@}} > ${tmpa}/postaction2" naked1: "echo {{@@ var3 @@}} > ${tmpa}/naked1" naked2: "echo {{@@ dvar3 @@}} > ${tmpa}/naked2" config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction1 - preaction2 - postaction1 - postaction2 - naked1 - naked2 profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "test" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --verbose # checks [ ! -e ${tmpa}/preaction1 ] && exit 1 [ ! -e ${tmpa}/preaction2 ] && exit 1 [ ! -e ${tmpa}/postaction1 ] && exit 1 [ ! -e ${tmpa}/postaction2 ] && exit 1 [ ! -e ${tmpa}/naked1 ] && exit 1 [ ! -e ${tmpa}/naked2 ] && exit 1 grep 'var1 var2 var3' ${tmpa}/preaction1 >/dev/null grep 'dvar1 dvar2 dvar3' ${tmpa}/preaction2 >/dev/null grep 'var1 var2 var3' ${tmpa}/postaction1 >/dev/null grep 'dvar1 dvar2 dvar3' ${tmpa}/postaction2 >/dev/null grep 'var1 var2 var3' ${tmpa}/naked1 >/dev/null grep 'dvar1 dvar2 dvar3' ${tmpa}/naked2 >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dyndotfilepaths.sh000077500000000000000000000040451436430751200205730ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dynamic variables in dotfile src/dst # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" dst=`echo ${tmpd} | rev` cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dynvariables: dpath: echo ${dst} | rev dotfiles: f_abc: dst: "{{@@ dpath @@}}/abc" src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ dpath @@}}" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${tmpd}/abc [ ! -e ${tmpd}/abc ] && echo "abc not installed dynamically" && exit 1 grep "^${tmpd}" ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dynextvariables.sh000077500000000000000000000072261436430751200206020ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dynamic external variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file extvars="${tmps}/variables.yaml" extdvars="${tmps}/dynvariables.yaml" pvars="${tmps}/p1_vars.yaml" pvarin="${tmps}/inprofile_vars.yaml" pvarout="${tmps}/outprofile_vars.yaml" cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - "{{@@ var1 @@}}iables.yaml" - "{{@@ dvar1 @@}}iables.yaml" - "{{@@ profile @@}}_vars.yaml" - "{{@@ xvar @@}}_vars.yaml" variables: var1: "var" xvar: outprofile dynvariables: dvar1: "echo dynvar" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: xvar: inprofile _EOF #cat ${cfg} # create the external variables file cat > ${extvars} << _EOF variables: vara: "extvar1" dynvariables: dvara: "echo extdvar1" _EOF cat > ${extdvars} << _EOF variables: varb: "extvar2" dynvariables: dvarb: "echo extdvar2" _EOF cat > ${pvars} << _EOF variables: pvar: "pvar1" dynvariables: pdvar: "echo pdvar1" _EOF cat > ${pvarin} << _EOF variables: test: profileok _EOF cat > ${pvarout} << _EOF variables: test: profilenotok _EOF # create the dotfile echo "var1: {{@@ var1 @@}}" > ${tmps}/dotfiles/abc echo "dvar1: {{@@ dvar1 @@}}" >> ${tmps}/dotfiles/abc # from var file 1 echo "vara: {{@@ vara @@}}" >> ${tmps}/dotfiles/abc echo "dvara: {{@@ dvara @@}}" >> ${tmps}/dotfiles/abc # from var file 2 echo "varb: {{@@ varb @@}}" >> ${tmps}/dotfiles/abc echo "dvarb: {{@@ dvarb @@}}" >> ${tmps}/dotfiles/abc # from var file 3 echo "pvar: {{@@ pvar @@}}" >> ${tmps}/dotfiles/abc echo "pdvar: {{@@ pdvar @@}}" >> ${tmps}/dotfiles/abc # from profile variable echo "test: {{@@ test @@}}" >> ${tmps}/dotfiles/abc #cat ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V cat ${tmpd}/abc grep '^var1: var' ${tmpd}/abc >/dev/null grep '^dvar1: dynvar' ${tmpd}/abc >/dev/null grep '^vara: extvar1' ${tmpd}/abc >/dev/null grep '^dvara: extdvar1' ${tmpd}/abc >/dev/null grep '^varb: extvar2' ${tmpd}/abc >/dev/null grep '^dvarb: extdvar2' ${tmpd}/abc >/dev/null grep '^pvar: pvar1' ${tmpd}/abc >/dev/null grep '^pdvar: pdvar1' ${tmpd}/abc >/dev/null grep '^test: profileok' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dyninclude.sh000077500000000000000000000046571436430751200175410ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dynamic includes # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF variables: var1: "_1" dynvariables: dvar1: "echo _2" config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def profiles: profile_1: dotfiles: - f_abc profile_2: dotfiles: - f_def profile_3: include: - profile{{@@ var1 @@}} profile_4: include: - profile{{@@ dvar1 @@}} _EOF #cat ${cfg} # create the dotfile c1="content:abc" echo "${c1}" > ${tmps}/dotfiles/abc c2="content:def" echo "${c2}" > ${tmps}/dotfiles/def # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p profile_3 --verbose # check dotfile exists [ ! -e ${tmpd}/abc ] && exit 1 #cat ${tmpd}/abc grep ${c1} ${tmpd}/abc >/dev/null || exit 1 # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p profile_4 --verbose # check dotfile exists [ ! -e ${tmpd}/def ] && exit 1 #cat ${tmpd}/def grep ${c2} ${tmpd}/def >/dev/null || exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/dynvariables.sh000077500000000000000000000054121436430751200200540ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test dynamic variables from yaml file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create a shell script export TESTENV="this is my testenv" scr=`mktemp --suffix='-dotdrop-tests' || mktemp -d` chmod +x ${scr} echo -e "#!/bin/bash\necho $TESTENV\n" >> ${scr} clear_on_exit "${scr}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: var1: "this is some test" var2: "the_dvar4" dynvariables: dvar1: head -1 ${cur}/helpers dvar2: "echo 'this is some test' | rev | tr ' ' ','" dvar3: ${scr} dvar4: "echo {{@@ var2 @@}} | rev" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ var1 @@}}" > ${tmps}/dotfiles/abc echo "{{@@ dvar1 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ dvar2 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ dvar3 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ dvar4 @@}}" >> ${tmps}/dotfiles/abc echo "test" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "-----" cat ${tmpd}/abc echo "-----" grep '^this is some test' ${tmpd}/abc >/dev/null grep '^# author: deadc0de6' ${tmpd}/abc >/dev/null grep '^tset,emos,si,siht' ${tmpd}/abc >/dev/null grep "^${TESTENV}" ${tmpd}/abc > /dev/null grep '^4ravd_eht' ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/env.sh000077500000000000000000000063261436430751200161660ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test import with env variables # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" tmpx=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpy=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpx}" clear_on_exit "${tmpy}" # create the dotfile mkdir -p ${tmpd}/adir echo "adir/file1" > ${tmpd}/adir/file1 echo "adir/fil2" > ${tmpd}/adir/file2 echo "file3" > ${tmpd}/file3 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles check_version: true dotfiles: profiles: _EOF #cat ${cfg} export DOTDROP_CONFIG="${cfg}" export DOTDROP_PROFILE="p1" export DOTDROP_VERBOSE= export DOTDROP_NOBANNER= export DOTDROP_TMPDIR="${tmpx}" export DOTDROP_WORKDIR="${tmpy}" export DOTDROP_WORKERS="1" # import cd ${ddpath} | ${bin} import -f ${tmpd}/adir cd ${ddpath} | ${bin} import -f ${tmpd}/file3 cat ${cfg} # ensure exists and is not link [ ! -d ${tmps}/dotfiles/${tmpd}/adir ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file1 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file2 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/file3 ] && echo "not a file" && exit 1 cat ${cfg} | grep ${tmpd}/adir >/dev/null 2>&1 cat ${cfg} | grep ${tmpd}/file3 >/dev/null 2>&1 nb=`cat ${cfg} | grep d_adir | wc -l` [ "${nb}" != "2" ] && echo 'bad config1' && exit 1 nb=`cat ${cfg} | grep f_file3 | wc -l` [ "${nb}" != "2" ] && echo 'bad config2' && exit 1 cntpre=`find ${tmps}/dotfiles -type f | wc -l` export DOTDROP_FORCE_NODEBUG= # compare cd ${ddpath} | ${bin} compare # install cd ${ddpath} | ${bin} install -f # reimport cd ${ddpath} | ${bin} import -f ${tmpd}/adir cd ${ddpath} | ${bin} import -f ${tmpd}/file3 cntpost=`find ${tmps}/dotfiles -type f | wc -l` [ "${cntpost}" != "${cntpre}" ] && echo "import failed" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/ext-actions.sh000077500000000000000000000052241436430751200176300ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test external actions # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" act="${tmps}/actions.yaml" cat > ${act} << _EOF actions: pre: preaction: echo 'pre' > ${tmpa}/pre post: postaction: echo 'post' > ${tmpa}/post nakedaction: echo 'naked' > ${tmpa}/naked overwrite: echo 'over' > ${tmpa}/write _EOF # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_actions: - ${tmps}/actions.yaml actions: overwrite: echo 'write' > ${tmpa}/write dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction - postaction - nakedaction - overwrite profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "test" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ ! -e ${tmpa}/pre ] && exit 1 grep pre ${tmpa}/pre >/dev/null echo "pre is ok" [ ! -e ${tmpa}/post ] && exit 1 grep post ${tmpa}/post >/dev/null echo "post is ok" [ ! -e ${tmpa}/naked ] && exit 1 grep naked ${tmpa}/naked >/dev/null echo "naked is ok" [ ! -e ${tmpa}/write ] && exit 1 grep over ${tmpa}/write >/dev/null echo "write is ok" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/extvariables.sh000077500000000000000000000116331436430751200200640ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test external variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file extvars="${tmps}/variables.yaml" cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - $(basename ${extvars}) variables: var1: "var1" var2: "{{@@ var1 @@}} var2" var3: "{{@@ var2 @@}} var3" var4: "{{@@ dvar4 @@}}" varx: "test" provar: "local" dynvariables: dvar1: "echo dvar1" dvar2: "{{@@ dvar1 @@}} dvar2" dvar3: "{{@@ dvar2 @@}} dvar3" dvar4: "echo {{@@ var3 @@}}" dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/{{@@ theprofile @@}} src: def profiles: p1: dotfiles: - f_abc - f_def variables: varx: profvarx provar: provar _EOF #cat ${cfg} # create the external variables file cat > ${extvars} << _EOF variables: varx: "exttest" dynvariables: evar1: "echo extevar1" theprofile: "echo {{@@ profile @@}}" _EOF # create the dotfile echo "var3: {{@@ var3 @@}}" > ${tmps}/dotfiles/abc echo "dvar3: {{@@ dvar3 @@}}" >> ${tmps}/dotfiles/abc echo "var4: {{@@ var4 @@}}" >> ${tmps}/dotfiles/abc echo "dvar4: {{@@ dvar4 @@}}" >> ${tmps}/dotfiles/abc echo "varx: {{@@ varx @@}}" >> ${tmps}/dotfiles/abc echo "evar1: {{@@ evar1 @@}}" >> ${tmps}/dotfiles/abc echo "provar: {{@@ provar @@}}" >> ${tmps}/dotfiles/abc echo "theprofile: {{@@ theprofile @@}}" >> ${tmps}/dotfiles/abc echo "theprofile: {{@@ theprofile @@}}" > ${tmps}/dotfiles/def #cat ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "check1" cat ${tmpd}/abc grep '^var3: var1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar3: dvar1 dvar2 dvar3' ${tmpd}/abc >/dev/null grep '^var4: echo var1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar4: var1 var2 var3' ${tmpd}/abc >/dev/null grep '^varx: profvarx' ${tmpd}/abc >/dev/null grep '^evar1: extevar1' ${tmpd}/abc >/dev/null grep '^provar: provar' ${tmpd}/abc >/dev/null grep '^theprofile: p1' ${tmpd}/abc >/dev/null # check def [ ! -e ${tmpd}/p1 ] && echo "def not created" && exit 1 grep '^theprofile: p1' ${tmpd}/p1 >/dev/null rm -f ${tmpd}/abc #cat ${tmpd}/abc cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - $(basename ${extvars}) dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: varx: profvarx vary: profvary _EOF #cat ${cfg} # create the external variables file cat > ${extvars} << _EOF variables: var1: "extvar1" varx: "exttest" var2: "{{@@ var1 @@}} var2" var3: "{{@@ var2 @@}} var3" var4: "{{@@ dvar4 @@}}" dynvariables: dvar1: "echo extdvar1" dvar2: "{{@@ dvar1 @@}} dvar2" dvar3: "{{@@ dvar2 @@}} dvar3" dvar4: "echo {{@@ var3 @@}}" _EOF # create the dotfile echo "var3: {{@@ var3 @@}}" > ${tmps}/dotfiles/abc echo "dvar3: {{@@ dvar3 @@}}" >> ${tmps}/dotfiles/abc echo "var4: {{@@ var4 @@}}" >> ${tmps}/dotfiles/abc echo "dvar4: {{@@ dvar4 @@}}" >> ${tmps}/dotfiles/abc echo "varx: {{@@ varx @@}}" >> ${tmps}/dotfiles/abc echo "vary: {{@@ vary @@}}" >> ${tmps}/dotfiles/abc #cat ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "test2" cat ${tmpd}/abc grep '^var3: extvar1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar3: extdvar1 dvar2 dvar3' ${tmpd}/abc >/dev/null grep '^var4: echo extvar1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar4: extvar1 var2 var3' ${tmpd}/abc >/dev/null grep '^varx: profvarx' ${tmpd}/abc >/dev/null grep '^vary: profvary' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/filter_file.sh000077500000000000000000000063061436430751200176600ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test jinja2 filters from filter_file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" filter_file=`mktemp` filter_file2=`mktemp` filter_file3=`mktemp` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${filter_file}" clear_on_exit "${filter_file2}" clear_on_exit "${filter_file3}" # create the config file cfg="${tmps}/config.yaml" cfgext="${tmps}/ext.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles filter_file: - ${filter_file} - ${filter_file2} import_configs: - ${cfgext} dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: filt: "{{@@ 'whatever' | filter1 @@}}" _EOF #cat ${cfg} cat > ${cfgext} << _EOF config: backup: true create: true dotpath: dotfiles filter_file: - ${filter_file3} profiles: dotfiles: _EOF cat << _EOF > ${filter_file} def filter1(arg1): return "filtered" def filter2(arg1, arg2=''): return arg2 _EOF cat << _EOF > ${filter_file2} def filter3(integer): return str(int(integer) - 10) _EOF cat << _EOF > ${filter_file3} def filter_ext(arg1): return "external" _EOF # create the dotfile echo "this is the test dotfile" > ${tmps}/dotfiles/abc # test imported function echo "{{@@ "abc" | filter1 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ "arg1" | filter2('arg2') @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ "13" | filter3() @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ "something" | filter_ext() @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ filt @@}}variable" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${tmpd}/abc grep '^filtered$' ${tmpd}/abc >/dev/null grep '^arg2$' ${tmpd}/abc >/dev/null grep '^3$' ${tmpd}/abc >/dev/null grep '^external$' ${tmpd}/abc >/dev/null set +e grep '^something$' ${tmpd}/abc >/dev/null && exit 1 set -e grep '^filteredvariable$' ${tmpd}/abc > /dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/force-actions.sh000077500000000000000000000055321436430751200201300ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # force actions # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: echo 'pre' > ${tmpa}/pre preaction2: echo 'pre2' > ${tmpa}/pre2 post: postaction: echo 'post' > ${tmpa}/post postaction2: echo 'post2' > ${tmpa}/post2 nakedaction: echo 'naked' > ${tmpa}/naked config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - preaction - postaction - nakedaction - preaction2 - postaction2 profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "test" > ${tmps}/dotfiles/abc # deploy the dotfile cp ${tmps}/dotfiles/abc ${tmpd}/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ -e ${tmpa}/pre ] && exit 1 [ -e ${tmpa}/post ] && exit 1 [ -e ${tmpa}/naked ] && exit 1 [ -e ${tmpa}/pre2 ] && exit 1 [ -e ${tmpa}/post2 ] && exit 1 # install and force cd ${ddpath} | ${bin} install -f -a -c ${cfg} -p p1 -V # checks [ ! -e ${tmpa}/pre ] && exit 1 grep pre ${tmpa}/pre >/dev/null [ ! -e ${tmpa}/post ] && exit 1 grep post ${tmpa}/post >/dev/null [ ! -e ${tmpa}/naked ] && exit 1 grep naked ${tmpa}/naked >/dev/null [ ! -e ${tmpa}/pre2 ] && exit 1 grep pre2 ${tmpa}/pre2 >/dev/null [ ! -e ${tmpa}/post2 ] && exit 1 grep post2 ${tmpa}/post2 >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/func_file.sh000077500000000000000000000071721436430751200173300ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test jinja2 functions from func_file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" func_file=`mktemp` func_file2=`mktemp` func_file3=`mktemp` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${func_file}" clear_on_exit "${func_file2}" clear_on_exit "${func_file3}" # create the config file cfg="${tmps}/config.yaml" cfgext="${tmps}/ext.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles func_file: - ${func_file} - ${func_file2} import_configs: - ${cfgext} dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: func: "{{@@ func1(False) @@}}" _EOF #cat ${cfg} cat > ${cfgext} << _EOF config: backup: true create: true dotpath: dotfiles func_file: - ${func_file3} dotfiles: profiles: _EOF cat << _EOF > ${func_file} def func1(something): if something: return True return False _EOF cat << _EOF > ${func_file2} def func2(inp): return not inp _EOF cat << _EOF > ${func_file3} def func3(inp): return 42 _EOF # create the dotfile echo "this is the test dotfile" > ${tmps}/dotfiles/abc # test imported function echo "{%@@ if func1(True) @@%}" >> ${tmps}/dotfiles/abc echo "this should exist" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{%@@ if not func1(False) @@%}" >> ${tmps}/dotfiles/abc echo "this should exist too" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{%@@ if func2(True) @@%}" >> ${tmps}/dotfiles/abc echo "nope" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{%@@ if func2(False) @@%}" >> ${tmps}/dotfiles/abc echo "yes" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{%@@ if func3("whatever") == 42 @@%}" >> ${tmps}/dotfiles/abc echo "externalok" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{{@@ func @@}}added" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${tmpd}/abc grep '^this should exist$' ${tmpd}/abc >/dev/null grep '^this should exist too$' ${tmpd}/abc >/dev/null grep '^yes$' ${tmpd}/abc >/dev/null grep '^externalok$' ${tmpd}/abc >/dev/null set +e grep '^nope$' ${tmpd}/abc >/dev/null && exit 1 set -e grep '^Falseadded$' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/global-compare-ignore.sh000077500000000000000000000051131436430751200215340ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test cmpignore # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/{program,config} touch ${tmpd}/program/a touch ${tmpd}/config/a # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/config # add files echo "[+] add files" touch ${tmpd}/program/b touch ${tmpd}/config/b # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/dotpath: dotfiles/a \ \ cmpignore:\n\ \ \ \ - "*/config/b"' ${cfg} > ${cfg2} cat ${cfg2} # expects one diff echo "[+] comparing with ignore in dotfile - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" = "0" ] && exit 1 set -e # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/dotpath: dotfiles/a \ \ cmpignore:\n\ \ \ \ - "*b"' ${cfg} > ${cfg2} cat ${cfg2} # expects no diff patt="*b" echo "[+] comparing with ignore in dotfile - 0 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" != "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/global-compare-negative-ignore.sh000077500000000000000000000062411436430751200233370ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test install cmpignore with negative ignores globally # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"{ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/{program,config} touch ${tmpd}/program/a touch ${tmpd}/config/a # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/config # add files echo "[+] add files" touch ${tmpd}/program/b touch ${tmpd}/config/b # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/dotpath: dotfiles/a\ \ \ cmpignore:\ \ \ \ \ - "*/config/*"\ \ \ \ \ - "!*/config/a"\ ' ${cfg} > ${cfg2} cat ${cfg2} # expects one diff echo "[+] comparing with ignore in dotfile - 1 diff" set +e cd ${ddpath} | ${bin} compare -c ${cfg2} --verbose [ "$?" = "0" ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/global-update-ignore.sh000077500000000000000000000045461436430751200214010ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test global ignore update # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt} mkdir -p ${dt}/a/{b,c} echo 'a' > ${dt}/a/b/abfile echo 'a' > ${dt}/a/c/acfile # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` cp -r ${dt}/a ${tmpd}/ clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles upignore: - "*/cfile" - "*/newfile" - "*/newdir" dotfiles: f_abc: dst: ${tmpd}/a src: a profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} #tree ${dt} # edit/add files echo "[+] edit/add files" touch ${tmpd}/a/newfile echo 'b' > ${tmpd}/a/c/acfile mkdir -p ${tmpd}/a/newdir/b touch ${tmpd}/a/newdir/b/c #tree ${tmpd}/a # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc #tree ${dt} # check files haven't been updated [ ! -e ${dt}/a/c/acfile ] && echo "acfile not found" && exit 1 set +e grep 'b' ${dt}/a/c/acfile || (echo "acfile not updated" && exit 1) set -e [ -e ${dt}/a/newfile ] && echo "newfile found" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/global-update-negative-ignore.sh000077500000000000000000000077021436430751200231760ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test global negative ignore update # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" mkdir -p ${basedir}/dotfiles/a/{b,c} echo 'a' > ${basedir}/dotfiles/a/b/abfile1 echo 'a' > ${basedir}/dotfiles/a/b/abfile2 echo 'a' > ${basedir}/dotfiles/a/b/abfile3 echo 'a' > ${basedir}/dotfiles/a/c/acfile # the dotfile to be updated tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` cp -r ${basedir}/dotfiles/a ${tmpd}/ clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles upignore: - "*/newdir/b/*" - "!*/newdir/b/d" - "*/abfile?" - "!*/abfile3" dotfiles: f_abc: dst: ${tmpd}/a src: a profiles: p1: dotfiles: - f_abc _EOF # edit/add files echo "[+] edit/add files" mkdir -p ${tmpd}/a/newdir/b echo 'b' > ${tmpd}/a/b/abfile1 echo 'b' > ${tmpd}/a/b/abfile2 echo 'b' > ${tmpd}/a/b/abfile3 echo 'b' > ${tmpd}/a/b/abfile4 touch ${tmpd}/a/newdir/b/{c,d} # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc # check files haven't been updated set +e grep 'a' ${basedir}/dotfiles/a/b/abfile1 >/dev/null 2>&1 || (echo "abfile1 should not have been updated" && exit 1) grep 'a' ${basedir}/dotfiles/a/b/abfile2 >/dev/null 2>&1 || (echo "abfile2 should not have been updated" && exit 1) grep 'b' ${basedir}/dotfiles/a/b/abfile3 >/dev/null 2>&1 || (echo "abfile3 was not updated" && exit 1) set -e [ -e ${basedir}/dotfiles/a/b/abfile4 ] && echo "abfile4 should not have been updated" && exit 1 [ -e ${basedir}/dotfiles/a/newdir/b/c ] && echo "newdir/b/c should not have been updated" && exit 1 [ ! -e ${basedir}/dotfiles/a/newdir/b/d ] && echo "newdir/b/d should have been updated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/globs.sh000077500000000000000000000052011436430751200164730ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # ensure imports allow globs # - import_actions # - import_configs # - import_variables # - profile import # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # temporary tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" ########### # test globs in import_actions ########### # create the action files actionsd="${tmps}/actions" mkdir -p ${actionsd} cat > ${actionsd}/action1.yaml << _EOF actions: fromaction1: echo "fromaction1" > ${tmpa}/fromaction1 _EOF cat > ${actionsd}/action2.yaml << _EOF actions: fromaction2: echo "fromaction2" > ${tmpa}/fromaction2 _EOF cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_actions: - ${actionsd}/* dotfiles: f_abc: dst: ${tmpd}/abc src: abc actions: - fromaction1 - fromaction2 profiles: p1: dotfiles: - f_abc _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 -V # checks [ ! -e ${tmpd}/abc ] && echo "dotfile not installed" && exit 1 [ ! -e ${tmpa}/fromaction1 ] && echo "action1 not executed" && exit 1 grep fromaction1 ${tmpa}/fromaction1 [ ! -e ${tmpa}/fromaction2 ] && echo "action2 not executed" && exit 1 grep fromaction2 ${tmpa}/fromaction2 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/header.sh000077500000000000000000000043441436430751200166240ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test header # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ header() @@}}" > ${tmps}/dotfiles/abc echo "{{@@ header('# ') @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ header('// ') @@}}" >> ${tmps}/dotfiles/abc echo "test" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 grep '^This dotfile is managed using dotdrop' ${tmpd}/abc >/dev/null grep '^# This dotfile is managed using dotdrop' ${tmpd}/abc >/dev/null grep '^// This dotfile is managed using dotdrop' ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/helpers000066400000000000000000000041401436430751200164140ustar00rootroot00000000000000# author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # file to be sourced from test scripts # # for i in *.sh; do ./$i >/dev/null 2>&1; find /tmp/ -maxdepth 1 -type f -iname 'tmp*' >> /tmp/$i.log; find /tmp/ -maxdepth 1 -type d -iname 'tmp.*-dotdrop-tests' >> /tmp/$i.log; find /tmp/ -maxdepth 1 -type d -iname 'dotdrop-*' >> /tmp/$i.log; wc -l /tmp/$i.log; [ "`wc -l /tmp/$i.log | awk '{print $1}'`" -gt "0" ] && break; done declare -a to_be_cleared # add a file/directory to be cleared # on exit # # $1: file path to clear clear_on_exit() { local len="${#to_be_cleared[*]}" to_be_cleared[${len}]="$1" if [ "${len}" = "0" ]; then # set trap trap on_exit EXIT fi } # clear files on_exit() { for i in "${to_be_cleared[@]}"; do rm -rf "${i}" done } # create a directory with sub-dirs and file # for tests # # $1: path of the parent directory to create # ret: set the variable token that allows to check create_dir() { dirs="a/aa a/ab a/ac b/ba c" mkdir -p ${1} for d in ${dirs}; do # create some dirs mkdir -p ${1}/${d} # create some files fn=`echo ${d} | sed 's#/#-#g'` f="${1}/${d}/${fn}" echo "${d}" > ${f} token=${f} done } # create a clean config file # # $1: path to save to create_conf() { cat > ${1} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF } # osx tricks # brew install coreutils gnu-sed if [[ $OSTYPE == 'darwin'* ]]; then mktemp() { gmktemp "$@" } stat() { gstat "$@" } sed() { gsed "$@" } wc() { gwc "$@" } date() { gdate "$@" } chmod() { gchmod "$@" } readlink() { greadlink "$@" } realpath() { grealpath "$@" } export -f mktemp export -f stat export -f sed export -f wc export -f date export -f chmod export -f readlink export -f realpath fi # workdir tricks # when tests are called without using the # top level tests.sh script which sets the workdir if [ -z "${DOTDROP_WORKDIR}" ]; then _workdir="/tmp/dotdrop-test-workdir" export DOTDROP_WORKDIR="${_workdir}" clear_on_exit "${_workdir}" fidotdrop-1.12.9/tests-ng/ignore-empty.sh000077500000000000000000000052251436430751200200120ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test empty template generation # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" # globally cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles ignoreempty: true dotfiles: d_d1: dst: ${tmpd}/d1 src: d1 profiles: p1: dotfiles: - d_d1 _EOF #cat ${cfg} # create the dotfile mkdir -p ${tmps}/dotfiles/d1 echo "{#@@ should be stripped @@#}" > ${tmps}/dotfiles/d1/empty echo "not empty" > ${tmps}/dotfiles/d1/notempty # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # test existence [ -e ${tmpd}/d1/empty ] && echo 'empty should not exist' && exit 1 [ ! -e ${tmpd}/d1/notempty ] && echo 'not empty should exist' && exit 1 # through the dotfile cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles ignoreempty: false dotfiles: d_d1: dst: ${tmpd}/d1 src: d1 ignoreempty: true profiles: p1: dotfiles: - d_d1 _EOF #cat ${cfg} # clean destination rm -rf ${tmpd}/* # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # test existence [ -e ${tmpd}/d1/empty ] && echo 'empty should not exist' && exit 1 [ ! -e ${tmpd}/d1/notempty ] && echo 'not empty should exist' && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-as.sh000077500000000000000000000102131436430751200172770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test basic import # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "~/.dotdrop.test" clear_on_exit "~/.dotdrop-dotfiles-test" # create the dotfile mkdir -p ${tmpd}/adir echo "adir/file1" > ${tmpd}/adir/file1 echo "adir/fil2" > ${tmpd}/adir/file2 echo "file3" > ${tmpd}/file3 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/adir cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/file3 echo "import --as dotfiles" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p2 -V ${tmpd}/adir --as ~/config/adir cd ${ddpath} | ${bin} import -f -c ${cfg} -p p2 -V ${tmpd}/file3 --as ~/config2/file3 cat ${cfg} set +e cd ${ddpath} | ${bin} import -f -c ${cfg} -p p2 -V ${tmpd}/adir --as ~/config/should_not && echo "dual dst imported" && exit 1 set -e cat ${cfg} | grep should_not && echo "dual dst imported" && exit 1 cat ${cfg} echo "ensure exists and is not link" [ ! -d ${tmps}/dotfiles/${tmpd}/adir ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file1 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file2 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/file3 ] && echo "not a file" && exit 1 echo "ensure --as are correctly imported" [ ! -d ${tmps}/dotfiles/config/adir ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/config/adir/file1 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/config/adir/file2 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/config2/file3 ] && echo "not a file" && exit 1 cat ${cfg} | grep ${tmpd}/adir >/dev/null 2>&1 cat ${cfg} | grep ${tmpd}/file3 >/dev/null 2>&1 cat ${cfg} | grep config/adir >/dev/null 2>&1 cat ${cfg} | grep config2/file3 >/dev/null 2>&1 nb=`cat ${cfg} | grep d_adir | wc -l` [ "${nb}" != "2" ] && echo 'bad config1' && exit 1 nb=`cat ${cfg} | grep f_file3 | wc -l` [ "${nb}" != "2" ] && echo 'bad config2' && exit 1 cat ${cfg} | grep "src: config/adir" || exit 1 cat ${cfg} | grep "src: config2/file3" || exit 1 # test import from sub in home mkdir -p ~/.dotdrop-dotfiles-test/{dotfiles,config} cfg=~/.dotdrop-dotfiles-test/config/config.yaml echo 'remove-me' > ~/.dotdrop.test cat > ${cfg} << _EOF config: backup: true banner: true create: true dotpath: ~/.dotdrop-dotfiles-test/dotfiles keepdot: false link_dotfile_default: nolink link_on_import: nolink longkey: true dotfiles: profiles: _EOF cd ${ddpath} | ${bin} import -f -b -c ${cfg} -p test -V ~/.dotdrop.test --as=~/.whatever #cat ${cfg} [ ! -e ~/.dotdrop-dotfiles-test/dotfiles/whatever ] && echo 'tild imported' && exit 1 cat ${cfg} | grep '~/.whatever' && echo 'import with tild failed' && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-configs.sh000077500000000000000000000124751436430751200203400ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # import config testing # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles-other # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg1="${tmps}/config1.yaml" cfg2="${tmps}/config2.yaml" cat > ${cfg1} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - ${cfg2} dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_zzz: dst: ${tmpd}/zzz src: zzz f_sub: dst: ${tmpd}/sub src: sub profiles: p0: include: - p2 p1: dotfiles: - f_abc p3: dotfiles: - f_zzz pup: include: - psubsub _EOF cat > ${cfg2} << _EOF config: backup: true create: true dotpath: dotfiles-other dotfiles: f_def: dst: ${tmpd}/def src: def f_ghi: dst: ${tmpd}/ghi src: ghi f_asub: dst: ${tmpd}/subdir/sub/asub src: subdir/sub/asub profiles: p2: dotfiles: - f_def - f_asub psubsub: dotfiles: - f_sub _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles/abc echo "def" > ${tmps}/dotfiles-other/def echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles-other/def echo "ghi" > ${tmps}/dotfiles-other/ghi echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles-other/ghi echo "zzz" > ${tmps}/dotfiles/zzz echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles/zzz echo "sub" > ${tmps}/dotfiles/sub echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles/sub mkdir -p ${tmps}/dotfiles-other/subdir/sub echo "subsub" > ${tmps}/dotfiles-other/subdir/sub/asub echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles-other/subdir/sub/asub # files comparison cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p0 | grep '^f_def' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p1 | grep '^f_abc' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p2 | grep '^f_def' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p3 | grep '^f_zzz' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p pup | grep '^f_sub' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p psubsub | grep '^f_sub' # test compare too cd ${ddpath} | ${bin} install -c ${cfg1} -p p2 -V -f cd ${ddpath} | ${bin} compare -c ${cfg1} -p p2 -V [ ! -s ${tmpd}/def ] && echo "def not installed" && exit 1 [ ! -s ${tmpd}/subdir/sub/asub ] && echo "asub not installed" && exit 1 # test with non-existing dotpath this time rm -rf ${tmps}/dotfiles rm -rf ${tmpd}/* cat > ${cfg1} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - ${cfg2} dotfiles: profiles: _EOF cat > ${cfg2} << _EOF config: backup: true create: true dotpath: dotfiles-other dotfiles: f_asub: dst: ${tmpd}/subdir/sub/asub src: subdir/sub/asub profiles: p2: dotfiles: - f_asub _EOF cd ${ddpath} | ${bin} install -c ${cfg1} -p p2 -V -f cd ${ddpath} | ${bin} compare -c ${cfg1} -p p2 -V # test with same profile defined in both rm -rf ${tmps}/dotfiles rm -rf ${tmpd}/* cat > ${cfg1} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - ${cfg2} dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF cat > ${cfg2} << _EOF config: backup: true create: true dotpath: dotfiles-other dotfiles: f_def: dst: ${tmpd}/def src: def profiles: p1: dotfiles: - f_def _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles/abc rm -f ${tmpd}/abc echo "def" > ${tmps}/dotfiles/def echo "{{@@ _dotfile_abs_dst @@}}" >> ${tmps}/dotfiles/def rm -f ${tmpd}/def # files listing echo "file listing" cd ${ddpath} | ${bin} files -c ${cfg1} -p p1 -G | grep '^f_abc' cd ${ddpath} | ${bin} files -c ${cfg1} -p p1 -G | grep '^f_def' # install and compare echo "installing ..." cd ${ddpath} | ${bin} install -c ${cfg1} -p p1 -V -f echo "comparing ..." cd ${ddpath} | ${bin} compare -c ${cfg1} -p p1 -V # check exists [ ! -s ${tmpd}/abc ] && echo "(same) abc not installed" && exit 1 [ ! -s ${tmpd}/def ] && echo "(same) def not installed" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-configs2.sh000077500000000000000000000051661436430751200204210ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # import config testing # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile sources tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" first="${tmps}/first" second="${tmps}/second" mkdir -p ${first} ${second} # create the config file cfg1="${first}/config.yaml" cfg2="${second}/config.yaml" cat > ${cfg1} << _EOF config: backup: true create: true dotpath: . import_configs: - ../second/config.yaml dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: include: - p1 dotfiles: - f_abc _EOF cat > ${cfg2} << _EOF config: backup: true create: true dotpath: . dotfiles: f_def: dst: ${tmpd}/def src: def profiles: p1: dotfiles: - f_def _EOF # create the source echo "abc" > ${first}/abc echo "{{@@ _dotfile_abs_dst @@}}" >> ${first}/abc echo "def" > ${second}/def echo "{{@@ _dotfile_abs_dst @@}}" >> ${second}/def # files comparison cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p0 | grep '^f_abc' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p0 | grep '^f_def' cd ${ddpath} | ${bin} files -c ${cfg1} -G -p p1 | grep '^f_def' cd ${ddpath} | ${bin} files -c ${cfg2} -G -p p1 | grep '^f_def' # test compare too cd ${ddpath} | ${bin} install -c ${cfg1} -p p0 -V -f cd ${ddpath} | ${bin} compare -c ${cfg1} -p p0 -V [ ! -s ${tmpd}/abc ] && echo "abc not installed" && exit 1 [ ! -s ${tmpd}/def ] && echo "def not installed" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-duplicate.sh000077500000000000000000000055061436430751200206570ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test import duplicates # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile touch ${tmpd}/.colors mkdir -p ${tmpd}/.mutt touch ${tmpd}/.mutt/colors # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles longkey: false dotfiles: f_colors: src: abc dst: abc f_mutt_colors: src: abc dst: abc f_`echo ${tmpd} | sed -e 's#^/\(.*\)$#\1#g' | sed 's#/#_#g' | tr '[:upper:]' '[:lower:]'`_colors: src: abc dst: abc f_`echo ${tmpd} | sed -e 's#^/tmp/\(.*\)$#\1#g' | sed 's#/#_#g' | tr '[:upper:]' '[:lower:]'`_colors: src: abc dst: abc f_`echo ${tmpd} | sed -e 's#^/\(.*\)$#\1#g' | sed 's#/#_#g' | tr '[:upper:]' '[:lower:]'`_mutt_colors: src: abc dst: abc f_`echo ${tmpd} | sed -e 's#^/tmp/\(.*\)$#\1#g' | sed 's#/#_#g' | tr '[:upper:]' '[:lower:]'`_mutt_colors: src: abc dst: abc profiles: _EOF cat ${cfg} # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/.mutt/colors cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/.colors cat ${cfg} # ensure exists and is not link [ ! -d ${tmps}/dotfiles/${tmpd}/.mutt ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/.mutt/colors ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/.colors ] && echo "not exist (2)" && exit 1 cat ${cfg} | grep ${tmpd}/.mutt/colors >/dev/null 2>&1 cat ${cfg} | grep ${tmpd}/.colors >/dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-ignore.sh000077500000000000000000000045361436430751200201720ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test ignore import # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmpd} #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # dotdrop directory mkdir -p ${tmpd}/a/{b,c} echo 'a' > ${tmpd}/a/b/abfile echo 'a' > ${tmpd}/a/c/acfile echo 'a' > ${tmpd}/a/b/newfile mkdir -p ${tmpd}/a/newdir echo 'a' > ${tmpd}/a/newdir/newfile # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles impignore: - "*/cfile" - "*/newfile" - "newdir" dotfiles: profiles: _EOF #cat ${cfg} # import echo "[+] import" cd ${ddpath} | ${bin} import -c ${cfg} -f --verbose --profile=p1 ${tmpd}/a [ -d ${tmps}/dotfiles/newdir ] && echo "newdir not ignored" && exit 1 [ -e ${tmps}/dotfiles/newdir/newfile ] && echo "newfile not ignored" && exit 1 [ -e ${tmps}/dotfiles/a/b/newfile ] && echo "newfile not ignored" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-include.sh000077500000000000000000000046301436430751200203250ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2023, deadc0de6 # # test import in profile which includes another # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile to import echo "file" > ${tmpd}/file # create the dotfiles already imported echo "already in" > ${tmps}/dotfiles/abc # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: include: - p1 p1: dotfiles: - f_abc _EOF cat ${cfg} cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep '^f_' | wc -l` [ "${cnt}" != "1" ] && echo "this is bad" && exit 1 # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p0 --verbose ${tmpd}/file [ ! -e ${tmps}/dotfiles/${tmpd}/file ] && echo "file not imported" && exit 1 # make sure file is in cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep '^f_file' | wc -l` [ "${cnt}" != "1" ] && echo "dotfiles not in config" && exit 1 # count cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 -b | grep '^f_' | wc -l` [ "${cnt}" != "2" ] && echo "not enough dotfile" exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-link-children.sh000077500000000000000000000075221436430751200214300ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test importing link_children # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # dotpath dotpath="${tmps}/dotfiles" mkdir -p ${dotpath} # create the dotfile to import dt="${tmpd}/directory" mkdir -p ${dt} # subdir dtsub1="${dt}/sub1" mkdir -p ${dtsub1} dtsub2="${dt}/sub2" mkdir -p ${dtsub2} dtsub3="${dtsub1}/subsub1" mkdir -p ${dtsub3} # files f1="${dt}/file" subf1="${dtsub1}/file" subf2="${dtsub2}/file" subf3="${dtsub3}/file" touch ${f1} ${subf1} ${subf2} ${subf3} # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V --link=link_children ${dt} # check is set to link_children cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "d_`basename ${dt}`" | grep ',link:link_children,' # checks file exists in dotpath [ ! -e ${dotpath}/${dt} ] && echo "dotfile not imported" && exit 1 [ ! -e ${dotpath}/${dtsub1} ] && echo "sub1 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${dtsub2} ] && echo "sub2 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${dtsub3} ] && echo "sub3 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${f1} ] && echo "f1 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${subf1} ] && echo "subf1 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${subf2} ] && echo "subf2 not found in dotpath" && exit 1 [ ! -e ${dotpath}/${subf3} ] && echo "subf3 not found in dotpath" && exit 1 # checks file exists in fs [ ! -e ${dt} ] && echo "dotfile not imported" && exit 1 [ ! -e ${dtsub1} ] && echo "sub1 not found in fs" && exit 1 [ ! -e ${dtsub2} ] && echo "sub2 not found in fs" && exit 1 [ ! -e ${dtsub3} ] && echo "sub3 not found in fs" && exit 1 [ ! -e ${f1} ] && echo "f1 not found in fs" && exit 1 [ ! -e ${subf1} ] && echo "subf1 not found in fs" && exit 1 [ ! -e ${subf2} ] && echo "subf2 not found in fs" && exit 1 [ ! -e ${subf3} ] && echo "subf3 not found in fs" && exit 1 # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks file have correct type in fs [ ! -h ${f1} ] && echo "f1 is not a symlink" && exit 1 [ -h ${subf1} ] && echo "subf1 is not a regular file" && exit 1 [ -h ${subf2} ] && echo "subf2 is not a regular file" && exit 1 [ -h ${subf3} ] && echo "subf3 is not a regular file" && exit 1 [ ! -h ${dtsub1} ] && echo "dtsub1 is not a symlink" && exit 1 [ ! -h ${dtsub2} ] && echo "dtsub2 is not a symlink" && exit 1 [ -h ${dtsub3} ] && echo "dtsub3 is not a regular directory" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-negative-ignore.sh000077500000000000000000000070541436430751200217700ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test negative ignore import # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` mkdir -p ${basedir}/dotfiles # the dotfile destination tmpd=`mkdir -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # dotdrop directory echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" mkdir -p ${tmpd}/a/{b,c} echo 'a' > ${tmpd}/a/b/abfile1 echo 'a' > ${tmpd}/a/b/abfile2 echo 'a' > ${tmpd}/a/b/abfile3 echo 'a' > ${tmpd}/a/c/acfile mkdir -p ${tmpd}/a/newdir/b touch ${tmpd}/a/newdir/b/{c,d} # create the config file cfg="${basedir}/config.yaml" cfg2="${basedir}/config2.yaml" create_conf ${cfg} # sets token sed '/dotpath: dotfiles/a\ \ \ impignore:\ \ \ \ \ - "*/newdir/b/*"\ \ \ \ \ - "!*/newdir/b/d"\ \ \ \ \ - "*/abfile?"\ \ \ \ \ - "!*/abfile3" ' ${cfg} > ${cfg2} # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg2} --verbose --profile=p1 ${tmpd}/a --as=~/a # check files haven't been imported [ -e ${basedir}/dotfiles/a/newdir/b/c ] && echo "newdir/b/c should not have been imported" && exit 1 [ ! -e ${basedir}/dotfiles/a/newdir/b/d ] && echo "newdir/b/d should have been imported" && exit 1 [ -e ${basedir}/dotfiles/a/b/abfile1 ] && echo "abfile1 should not have been imported" && exit 1 [ -e ${basedir}/dotfiles/a/b/abfile2 ] && echo "abfile2 should not have been imported" && exit 1 [ ! -e ${basedir}/dotfiles/a/b/abfile3 ] && echo "abfile3 should have been imported" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-non-existing.sh000077500000000000000000000125011436430751200213200ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test import not existing # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile echo "test" > ${tmps}/dotfiles/abc # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - /variables/does/not/exist:optional - /variables/does/not/::exist:optional - /variables/*/not/exist:optional import_actions: - /actions/does/not/exist:optional - /actions/does/not/::exist:optional - /actions/does/*/exist:optional import_configs: - /configs/does/not/exist:optional - /configs/does/not/::exist:optional - /configs/does/not/*:optional dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # dummy call cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - /variables/does/not/exist dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "variables" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - /variables/does/not/exist:with/separator dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "variables with separator" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - /variables/*/not/exist dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "variables glob" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_actions: - /actions/does/not/exist dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "actions" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_actions: - /actions/does/not:exist/with/separator dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "actions with separator" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_actions: - /actions/does/*/exist dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "actions glob" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - /configs/does/not/exist dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "configs" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - /configs/does:not/exist/with/separator dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "configs with separator" && exit 1 set -e cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - /configs/does/not/* dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # dummy call set +e cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V [ "$?" = "0" ] && echo "configs glob" && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-profile-dotfiles.sh000077500000000000000000000053761436430751200221610ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test the use of the keyword "import" in profiles # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` extdotfiles="${tmps}/df_p1.yaml" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" dynextdotfiles_name="d_uid_dynvar" dynextdotfiles="${tmps}/ext_${dynextdotfiles_name}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dynvariables: d_uid: "echo ${dynextdotfiles_name}" dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def f_xyz: dst: ${tmpd}/xyz src: xyz f_dyn: dst: ${tmpd}/dyn src: dyn profiles: p1: dotfiles: - f_abc import: - $(basename ${extdotfiles}) - "ext_{{@@ d_uid @@}}" _EOF # create the external dotfile file cat > ${extdotfiles} << _EOF dotfiles: - f_def - f_xyz _EOF cat > ${dynextdotfiles} << _EOF dotfiles: - f_dyn _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc echo "def" > ${tmps}/dotfiles/def echo "xyz" > ${tmps}/dotfiles/xyz echo "dyn" > ${tmps}/dotfiles/dyn # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # checks [ ! -e ${tmpd}/abc ] && exit 1 [ ! -e ${tmpd}/def ] && exit 1 [ ! -e ${tmpd}/xyz ] && exit 1 [ ! -e ${tmpd}/dyn ] && exit 1 echo 'file found' grep 'abc' ${tmpd}/abc >/dev/null 2>&1 grep 'def' ${tmpd}/def >/dev/null 2>&1 grep 'xyz' ${tmpd}/xyz >/dev/null 2>&1 grep 'dyn' ${tmpd}/dyn >/dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-subfile.sh000077500000000000000000000041661436430751200203370ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test import file in directory # after having imported directory # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile mkdir -p ${tmpd}/adir echo "first" > ${tmpd}/adir/file1 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import dir cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/adir # change the file echo "second" >> ${tmpd}/adir/file1 # import file cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/adir/file1 # test #cat ${tmps}/dotfiles/${tmpd}/adir/file1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file1 ] && echo "not exist" && exit 1 grep 'second' ${tmps}/dotfiles/${tmpd}/adir/file1 >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-to-no-profile.sh000077500000000000000000000065321436430751200213770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test import to no profile (using ALL keyword) # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile echo "file1" > ${tmpd}/file1 echo "file2" > ${tmpd}/file2 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} noprofile="ALL" ################################## # import with profile from arg cd ${ddpath} | ${bin} import -f -c ${cfg} -p "${noprofile}" -V ${tmpd}/file1 cat ${cfg} # ensure exists and is not link [ ! -e ${tmps}/dotfiles/${tmpd}/file1 ] && echo "file not imported" && exit 1 # ensure present in config cat ${cfg} | grep ${tmpd}/file1 >/dev/null 2>&1 nb=`cat ${cfg} | grep f_file1 | wc -l` [ "${nb}" != "1" ] && echo 'bad config' && exit 1 cntpre=`find ${tmps}/dotfiles -type f | wc -l` # reimport set +e cd ${ddpath} | ${bin} import -f -c ${cfg} -p "${noprofile}" -V ${tmpd}/file1 set -e cat ${cfg} cntpost=`find ${tmps}/dotfiles -type f | wc -l` [ "${cntpost}" != "${cntpre}" ] && echo "imported twice" && exit 1 nb=`cat ${cfg} | grep "dst: ${tmpd}/file1" | wc -l` [ "${nb}" != "1" ] && echo 'imported twice in config' && exit 1 ################################## # import with profile from env export DOTDROP_PROFILE="${noprofile}" cd ${ddpath} | ${bin} import -f -c ${cfg} -V ${tmpd}/file2 cat ${cfg} # ensure exists and is not link [ ! -e ${tmps}/dotfiles/${tmpd}/file2 ] && echo "file not imported" && exit 1 # ensure present in config cat ${cfg} | grep ${tmpd}/file2 >/dev/null 2>&1 nb=`cat ${cfg} | grep f_file2 | wc -l` [ "${nb}" != "1" ] && echo 'bad config' && exit 1 cntpre=`find ${tmps}/dotfiles -type f | wc -l` # reimport set +e cd ${ddpath} | ${bin} import -f -c ${cfg} -V ${tmpd}/file2 set -e cat ${cfg} cntpost=`find ${tmps}/dotfiles -type f | wc -l` [ "${cntpost}" != "${cntpre}" ] && echo "imported twice" && exit 1 nb=`cat ${cfg} | grep "dst: ${tmpd}/file2" | wc -l` [ "${nb}" != "1" ] && echo 'imported twice in config' && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-with-empty.sh000077500000000000000000000050511436430751200210070ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test import new dotfiles with empty dst/src on existing dotfiles # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the temp directory tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # create a dotfile dftoimport="${tmpd}/a_dotfile" echo 'some content' > ${dftoimport} # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: /tmp/x dst: f_y: src: dst: /tmp/y f_z: src: dst: f_l: src: dst: link: link f_lc: src: dst: link: link_children profiles: p1: dotfiles: - f_x - f_y - f_z - f_l - f_lc _EOF echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 --verbose ${dftoimport} [ "$?" != "0" ] && exit 1 echo "[+] install" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^5 dotfile(s) installed.$' rm -f ${dftoimport} cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^6 dotfile(s) installed.$' nb=`cd ${ddpath} | ${bin} files -c ${cfg} -p p1 --verbose | grep '^[a-zA-Z]' | grep -v '^Dotfile(s)' | wc -l` [ "${nb}" != "6" ] && echo "error in dotfile list (${nb} VS 6)" && exit 1 #cat ${cfg} echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import-with-trans.sh000077500000000000000000000115401436430751200210000ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test transformations for import # returns 1 in case of error # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF trans_read: base64: "cat {0} | base64 -d > {1}" decompress: "mkdir -p {1} && tar -xf {0} -C {1}" decrypt: "echo {{@@ profile @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -d {0} > {1}" trans_write: base64: "cat {0} | base64 > {1}" compress: "tar -cf {1} -C {0} ." encrypt: "echo {{@@ profile @@}} | gpg -q --batch --yes --passphrase-fd 0 --no-tty -o {1} -c {0}" config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # tokens token="test-base64" tokend="compressed archive" tokenenc="encrypted" # create the dotfiles echo ${token} > ${tmpd}/abc mkdir -p ${tmpd}/def/a echo ${tokend} > ${tmpd}/def/a/file echo ${tokenenc} > ${tmpd}/ghi ########################### # test import ########################### echo "[+] run import" # import file (to base64) cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -b -V --transw=base64 --transr=base64 ${tmpd}/abc # import directory (to compress) cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -b -V --transw=compress --transr=decompress ${tmpd}/def # import file (to encrypt) cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -b -V --transw=encrypt --transr=decrypt ${tmpd}/ghi # check file imported in dotpath [ ! -e ${tmps}/dotfiles/${tmpd}/abc ] && echo "abc does not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/def ] && echo "def does not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/ghi ] && echo "ghi does not exist" && exit 1 # check content in dotpath echo "checking content" file ${tmps}/dotfiles/${tmpd}/abc | grep -i 'text' cat ${tmpd}/abc | base64 > ${tmps}/test-abc diff ${tmps}/dotfiles/${tmpd}/abc ${tmps}/test-abc file ${tmps}/dotfiles/${tmpd}/def | grep -i 'tar' tar -cf ${tmps}/test-def -C ${tmpd}/def . diff ${tmps}/dotfiles/${tmpd}/def ${tmps}/test-def file ${tmps}/dotfiles/${tmpd}/ghi | grep -i 'gpg symmetrically encrypted data' echo p1 | gpg -q --batch --yes --passphrase-fd 0 --no-tty -d ${tmps}/dotfiles/${tmpd}/ghi > ${tmps}/test-ghi diff ${tmps}/test-ghi ${tmpd}/ghi # check is imported in config echo "checking imported in config" cd ${ddpath} | ${bin} -p p1 -c ${cfg} files cd ${ddpath} | ${bin} -p p1 -c ${cfg} files | grep '^f_abc' cd ${ddpath} | ${bin} -p p1 -c ${cfg} files | grep '^d_def' cd ${ddpath} | ${bin} -p p1 -c ${cfg} files | grep '^f_ghi' # check has trans_write and trans_read in config echo "checking trans_write is set in config" echo "--------------" cat ${cfg} echo "--------------" cat ${cfg} | grep -A 4 'f_abc:' | grep 'trans_write: base64' cat ${cfg} | grep -A 4 'd_def:' | grep 'trans_write: compress' cat ${cfg} | grep -A 4 'f_ghi:' | grep 'trans_write: encrypt' cat ${cfg} | grep -A 4 'f_abc:' | grep 'trans_read: base64' cat ${cfg} | grep -A 4 'd_def:' | grep 'trans_read: decompress' cat ${cfg} | grep -A 4 'f_ghi:' | grep 'trans_read: decrypt' # install these echo "install and check" rm ${tmpd}/abc rm -r ${tmpd}/def rm ${tmpd}/ghi cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # test exist echo "check exist" [ ! -e ${tmpd}/abc ] && exit 1 [ ! -d ${tmpd}/def/a ] && exit 1 [ ! -e ${tmpd}/def/a/file ] && exit 1 [ ! -e ${tmpd}/ghi ] && exit 1 # test content echo "check content" cat ${tmpd}/abc cat ${tmpd}/abc | grep "${token}" cat ${tmpd}/def/a/file cat ${tmpd}/def/a/file | grep "${tokend}" cat ${tmpd}/ghi | grep "${tokenenc}" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/import.sh000077500000000000000000000066561436430751200167160ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test basic import # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile mkdir -p ${tmpd}/adir echo "adir/file1" > ${tmpd}/adir/file1 echo "adir/fil2" > ${tmpd}/adir/file2 echo "file3" > ${tmpd}/file3 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/adir cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/file3 cat ${cfg} # ensure exists and is not link [ ! -d ${tmps}/dotfiles/${tmpd}/adir ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file1 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/adir/file2 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${tmpd}/file3 ] && echo "not a file" && exit 1 cat ${cfg} | grep ${tmpd}/adir >/dev/null 2>&1 cat ${cfg} | grep ${tmpd}/file3 >/dev/null 2>&1 nb=`cat ${cfg} | grep d_adir | wc -l` [ "${nb}" != "2" ] && echo 'bad config1' && exit 1 nb=`cat ${cfg} | grep f_file3 | wc -l` [ "${nb}" != "2" ] && echo 'bad config2' && exit 1 cntpre=`find ${tmps}/dotfiles -type f | wc -l` # reimport cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/adir cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/file3 cntpost=`find ${tmps}/dotfiles -type f | wc -l` [ "${cntpost}" != "${cntpre}" ] && echo "import issue" && exit 1 ####################################### # import directory with named pipe cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # create the dotfile d="${tmpd}/with_named_pipe" mkdir -p ${d} echo "file1" > ${d}/file1 echo "fil2" > ${d}/file2 mkfifo ${d}/fifo # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p2 -V ${d} # ensure exists and is not link [ ! -d ${tmps}/dotfiles/${d} ] && echo "not a directory" && exit 1 [ ! -e ${tmps}/dotfiles/${d}/file1 ] && echo "not exist" && exit 1 [ ! -e ${tmps}/dotfiles/${d}/file2 ] && echo "not exist" && exit 1 cat ${cfg} | grep ${d} >/dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/imported-configs-variables.sh000077500000000000000000000062221436430751200226100ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test external config's variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file extcfg="${tmps}/ext-config.yaml" cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - $(basename ${extcfg}) variables: varx: "test" provar: "local" dynvariables: dvarx: "echo dtest" dprovar: "echo dlocal" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: varx: profvarx provar: provar dynvariables: dvarx: echo dprofvarx dprovar: echo dprovar _EOF cat ${cfg} # create the external variables file cat > ${extcfg} << _EOF config: profiles: p2: dotfiles: - f_abc variables: varx: extprofvarx provar: extprovar dynvariables: dvarx: echo extdprofvarx dprovar: echo extdprovar dotfiles: _EOF ls -l ${extcfg} cat ${extcfg} # create the dotfile echo "varx: {{@@ varx @@}}" > ${tmps}/dotfiles/abc echo "provar: {{@@ provar @@}}" >> ${tmps}/dotfiles/abc echo "dvarx: {{@@ dvarx @@}}" >> ${tmps}/dotfiles/abc echo "dprovar: {{@@ dprovar@@}}" >> ${tmps}/dotfiles/abc #cat ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p2 -V echo "test1" cat ${tmpd}/abc grep '^varx: extprofvarx' ${tmpd}/abc >/dev/null grep '^provar: extprovar' ${tmpd}/abc >/dev/null grep '^dvarx: extdprofvarx' ${tmpd}/abc >/dev/null grep '^dprovar: extdprovar' ${tmpd}/abc >/dev/null rm -f ${tmpd}/abc cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V echo "test2" cat ${tmpd}/abc grep '^varx: profvarx' ${tmpd}/abc >/dev/null grep '^provar: provar' ${tmpd}/abc >/dev/null grep '^dvarx: dprofvarx' ${tmpd}/abc >/dev/null grep '^dprovar: dprovar' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/imported-variables-from-config.sh000077500000000000000000000044471436430751200233750ustar00rootroot00000000000000#!/usr/bin/env bash # author: davla (https://github.com/davls) # Copyright (c) 2020, davla # # test variables imported from config and used in the importing yaml config # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" subcfg="${tmps}/subconfig.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_configs: - ${subcfg} dotfiles: f_abc: dst: ${tmpd}/abc src: '{{@@ abc_dyn_src @@}}{{@@ abc_src @@}}' profiles: p1: dotfiles: - f_abc _EOF cat ${cfg} # create the subconfig file cat > ${subcfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: abc_src: c dynvariables: abc_dyn_src: 'echo ab' dotfiles: [] profiles: [] _EOF # create the dotfile dirname ${tmps}/dotfiles/abc | xargs mkdir -p cat > ${tmps}/dotfiles/abc << _EOF Hell yeah _EOF # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # test file existence and content [ -f "${tmpd}/abc" ] || { echo 'Dotfile not installed' exit 1 } echo "OK" exit 0 dotdrop-1.12.9/tests-ng/include-actions.sh000077500000000000000000000141641436430751200204560ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test the use of the keyword "include" # with action inheritance # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF actions: pre: preaction: echo 'pre' >> ${tmpa}/pre preaction2: echo 'pre2' >> ${tmpa}/pre2 post: postaction: echo 'post' >> ${tmpa}/post postaction2: echo 'post2' >> ${tmpa}/post2 nakedaction: echo 'naked' >> ${tmpa}/naked config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: include: - p3 p1: dotfiles: - f_abc actions: - preaction - postaction p2: include: - p1 actions: - preaction2 - postaction2 p3: include: - p2 actions: - nakedaction _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "test" > ${tmps}/dotfiles/abc # install echo "PROFILE p2" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p2 -V # checks [ ! -e ${tmpa}/pre ] && echo "pre not found" && exit 1 nb=`wc -l ${tmpa}/pre | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre executed multiple times" && exit 1 [ ! -e ${tmpa}/pre2 ] && echo "pre2 not found" && exit 1 nb=`wc -l ${tmpa}/pre2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre2 executed multiple times" && exit 1 [ ! -e ${tmpa}/post ] && echo "post not found" && exit 1 nb=`wc -l ${tmpa}/post | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post executed multiple times" && exit 1 [ ! -e ${tmpa}/post2 ] && echo "post2 not found" && exit 1 nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post2 executed multiple times" && exit 1 # install rm -f ${tmpa}/pre ${tmpa}/pre2 ${tmpa}/post ${tmpa}/post2 ${tmpa}/naked rm -f ${tmpd}/abc echo "PROFILE p3" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p3 -V # checks [ ! -e ${tmpa}/pre ] && echo "pre not found" && exit 1 nb=`wc -l ${tmpa}/pre | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre executed multiple times" && exit 1 [ ! -e ${tmpa}/pre2 ] && echo "pre2 not found" && exit 1 nb=`wc -l ${tmpa}/pre2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre2 executed multiple times" && exit 1 [ ! -e ${tmpa}/post ] && echo "post not found" && exit 1 nb=`wc -l ${tmpa}/post | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post executed multiple times" && exit 1 [ ! -e ${tmpa}/post2 ] && echo "post2 not found" && exit 1 nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post2 executed multiple times" && exit 1 [ ! -e ${tmpa}/naked ] && echo "naked not found" && exit 1 nb=`wc -l ${tmpa}/naked | awk '{print $1}'` [ "${nb}" != "1" ] && echo "naked executed multiple times" && exit 1 # install rm -f ${tmpa}/pre ${tmpa}/pre2 ${tmpa}/post ${tmpa}/post2 ${tmpa}/naked rm -f ${tmpd}/abc echo "PROFILE p0" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 -V # checks [ ! -e ${tmpa}/pre ] && echo "pre not found" && exit 1 nb=`wc -l ${tmpa}/pre | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre executed multiple times" && exit 1 [ ! -e ${tmpa}/pre2 ] && echo "pre2 not found" && exit 1 nb=`wc -l ${tmpa}/pre2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre2 executed multiple times" && exit 1 [ ! -e ${tmpa}/post ] && echo "post not found" && exit 1 nb=`wc -l ${tmpa}/post | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post executed multiple times" && exit 1 [ ! -e ${tmpa}/post2 ] && echo "post2 not found" && exit 1 nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post2 executed multiple times" && exit 1 [ ! -e ${tmpa}/naked ] && echo "naked not found" && exit 1 nb=`wc -l ${tmpa}/naked | awk '{print $1}'` [ "${nb}" != "1" ] && echo "naked executed multiple times" && exit 1 # install without verbose rm -f ${tmpa}/pre ${tmpa}/pre2 ${tmpa}/post ${tmpa}/post2 ${tmpa}/naked rm -f ${tmpd}/abc echo "PROFILE p0 without verbose" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 # checks [ ! -e ${tmpa}/pre ] && echo "pre not found" && exit 1 nb=`wc -l ${tmpa}/pre | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre executed multiple times" && exit 1 [ ! -e ${tmpa}/pre2 ] && echo "pre2 not found" && exit 1 nb=`wc -l ${tmpa}/pre2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "pre2 executed multiple times" && exit 1 [ ! -e ${tmpa}/post ] && echo "post not found" && exit 1 nb=`wc -l ${tmpa}/post | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post executed multiple times" && exit 1 [ ! -e ${tmpa}/post2 ] && echo "post2 not found" && exit 1 nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "post2 executed multiple times" && exit 1 [ ! -e ${tmpa}/naked ] && echo "naked not found" && exit 1 nb=`wc -l ${tmpa}/naked | awk '{print $1}'` [ "${nb}" != "1" ] && echo "naked executed multiple times" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/include-order.sh000077500000000000000000000066121436430751200201300ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test the use of the keyword "include" # that has to be ordered # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # temporary tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" export DOTDROP_WORKERS=1 # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles actions: pre: first: 'echo first > ${tmpa}/cookie' second: 'sleep 1; echo second >> ${tmpa}/cookie' third: 'sleep 1; echo third >> ${tmpa}/cookie' dotfiles: f_first: dst: ${tmpd}/first src: first actions: - first f_second: dst: ${tmpd}/second src: second actions: - second f_third: dst: ${tmpd}/third src: third actions: - third profiles: p0: dotfiles: - f_first include: - second - third second: dotfiles: - f_second third: dotfiles: - f_third _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "first" > ${tmps}/dotfiles/first echo "second" > ${tmps}/dotfiles/second echo "third" > ${tmps}/dotfiles/third attempts="3" for ((i=0;i<${attempts};i++)); do # install cd ${ddpath} | ${bin} install -w 1 -f -c ${cfg} -p p0 -V # checks timestamp echo "first timestamp: `stat -c %y ${tmpd}/first`" echo "second timestamp: `stat -c %y ${tmpd}/second`" echo "third timestamp: `stat -c %y ${tmpd}/third`" ts_first=`date "+%s" -d "$(stat -c %y ${tmpd}/first)"` ts_second=`date "+%s" -d "$(stat -c %y ${tmpd}/second)"` ts_third=`date "+%s" -d "$(stat -c %y ${tmpd}/third)"` #echo "first ts: ${ts_first}" #echo "second ts: ${ts_second}" #echo "third ts: ${ts_third}" [ "${ts_first}" -ge "${ts_second}" ] && echo "second created before first" && exit 1 [ "${ts_second}" -ge "${ts_third}" ] && echo "third created before second" && exit 1 # check cookie cat ${tmpa}/cookie content=`cat ${tmpa}/cookie | xargs` [ "${content}" != "first second third" ] && echo "bad cookie" && exit 1 # clean rm ${tmpa}/cookie rm ${tmpd}/first ${tmpd}/second ${tmpd}/third done echo "OK" exit 0 dotdrop-1.12.9/tests-ng/include-variables.sh000077500000000000000000000044351436430751200207660ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test the use of the keyword "include" # and (dyn)variables precedence # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: var: nopv dynvariables: dvar: "echo nopdv" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: variables: var: p0v dynvariables: dvar: "echo p0dv" include: - p1 p1: dotfiles: - f_abc variables: var: p1v dynvariables: dvar: "echo p1dv" _EOF #cat ${cfg} # create the source mkdir -p ${tmps}/dotfiles/ echo "head" > ${tmps}/dotfiles/abc echo "{{@@ var @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ dvar @@}}" >> ${tmps}/dotfiles/abc echo "tail" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 --verbose #cat ${tmpd}/abc grep 'p0v' ${tmpd}/abc grep 'p0dv' ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/include.sh000077500000000000000000000055141436430751200170170ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test the use of the keyword "include" # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles actions: pre: preaction: touch ${tmpd}/action.pre postaction: touch ${tmpd}/action.post dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: dotfiles: include: - p3 p1: actions: - preaction - postaction dotfiles: - f_abc p2: include: - p1 p3: include: - p2 _EOF cat ${cfg} # create the source mkdir -p ${tmps}/dotfiles/ echo "test" > ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 --verbose [ ! -e ${tmpd}/action.pre ] && exit 1 [ ! -e ${tmpd}/action.post ] && exit 1 # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 cd ${ddpath} | ${bin} compare -c ${cfg} -p p2 cd ${ddpath} | ${bin} compare -c ${cfg} -p p3 cd ${ddpath} | ${bin} compare -c ${cfg} -p p0 # list cd ${ddpath} | ${bin} files -c ${cfg} -p p1 | grep f_abc cd ${ddpath} | ${bin} files -c ${cfg} -p p2 | grep f_abc cd ${ddpath} | ${bin} files -c ${cfg} -p p3 | grep f_abc cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep f_abc cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p0 | grep f_abc | wc -l` [ "${cnt}" != "1" ] && echo "dotfiles displayed more than once" && exit 1 # count cnt=`cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -b | grep '^f_' | wc -l` [ "${cnt}" != "1" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/inst-link-default.sh000077500000000000000000000077631436430751200207360ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test link_dotfile_default # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "test link_dotfile_default 1" > ${tmps}/dotfiles/abc/file1 echo "test link_dotfile_default 2" > ${tmps}/dotfiles/abc/file2 echo "should be linked" > ${tmps}/dotfiles/def # create a shell script # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: d_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - d_abc _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and is not link [ ! -d ${tmpd}/abc ] && echo "not a directory" && exit 1 [ -h ${tmpd}/abc ] && echo "not a regular file" && exit 1 [ ! -e ${tmpd}/abc/file1 ] && echo "not exist" && exit 1 [ -h ${tmpd}/abc/file1 ] && echo "not a regular file" && exit 1 [ ! -e ${tmpd}/abc/file2 ] && echo "not exist" && exit 1 [ -h ${tmpd}/abc/file2 ] && echo "not a regular file" && exit 1 rm -rf ${tmpd}/abc cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: link dotfiles: d_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def profiles: p1: dotfiles: - d_abc - f_def _EOF # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and parent is a link [ ! -e ${tmpd}/abc ] && echo "not exist" && exit 1 [ ! -h ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -e ${tmpd}/abc/file1 ] && echo "not exist" && exit 1 [ -h ${tmpd}/abc/file1 ] && echo "not a regular file" && exit 1 [ ! -e ${tmpd}/abc/file2 ] && echo "not exist" && exit 1 [ -h ${tmpd}/abc/file2 ] && echo "not a regular file" && exit 1 rm -rf ${tmpd}/abc [ ! -e ${tmpd}/def ] && echo "not exist" && exit 1 [ ! -h ${tmpd}/def ] && echo "not a symlink" && exit 1 rm -f ${tmpd}/def cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: link_children dotfiles: d_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - d_abc _EOF # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and children are links [ ! -e ${tmpd}/abc ] && echo "not exist" && exit 1 [ -h ${tmpd}/abc ] && echo "not a regular file" && exit 1 [ ! -e ${tmpd}/abc/file1 ] && echo "not exist" && exit 1 [ ! -h ${tmpd}/abc/file1 ] && echo "not a symlink" && exit 1 [ ! -e ${tmpd}/abc/file2 ] && echo "not exist" && exit 1 [ ! -h ${tmpd}/abc/file2 ] && echo "not a symlink" && exit 1 rm -rf ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install-empty.sh000077500000000000000000000036751436430751200202040ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test install empty dst or empty src # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" clear_on_exit "${basedir}" # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: /tmp/x dst: f_y: src: dst: /tmp/y f_z: src: dst: f_l: src: dst: link: link f_lc: src: dst: link: link_children profiles: p1: dotfiles: - f_x - f_y - f_z - f_l - f_lc _EOF echo "[+] install" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^5 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install-ignore.sh000077500000000000000000000127211436430751200203210ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test install ignore absolute/relative # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmps="${basedir}" clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/{program,config,vscode} echo "some data" > ${tmpd}/program/a echo "some data" > ${tmpd}/config/a echo "some data" > ${tmpd}/vscode/extensions.txt echo "some data" > ${tmpd}/vscode/keybindings.json # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/config cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/vscode # add files on filesystem echo "[+] add files" echo "new data" > ${basedir}/dotfiles/${tmpd}/README.md echo "new data" > ${basedir}/dotfiles/${tmpd}/vscode/README.md echo "new data" > ${basedir}/dotfiles/${tmpd}/program/README.md mkdir -p ${basedir}/dotfiles/${tmpd}/readmes echo "new data" > ${basedir}/dotfiles/${tmpd}/readmes/README.md # install rm -rf ${tmpd} echo "[+] install normal" cd ${ddpath} | ${bin} install --showdiff -c ${cfg} --verbose -f [ "$?" != "0" ] && exit 1 nb=`find ${tmpd} -iname 'README.md' | wc -l` echo "(1) found ${nb} README.md file(s)" [ "${nb}" != "2" ] && exit 1 # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_program:/a \ \ \ \ instignore:\n\ \ \ \ - "README.md"' ${cfg} > ${cfg2} cat ${cfg2} # install rm -rf ${tmpd} echo "[+] install with ignore in dotfile" cd ${ddpath} | ${bin} install -c ${cfg2} --verbose -f [ "$?" != "0" ] && exit 1 nb=`find ${tmpd} -iname 'README.md' | wc -l` echo "(2) found ${nb} README.md file(s)" [ "${nb}" != "1" ] && exit 1 # adding ignore in config cfg2="${basedir}/config2.yaml" sed '/^config:/a \ \ instignore:\n\ \ - "README.md"' ${cfg} > ${cfg2} cat ${cfg2} # install rm -rf ${tmpd} echo "[+] install with ignore in config" cd ${ddpath} | ${bin} install -c ${cfg2} --verbose -f [ "$?" != "0" ] && exit 1 nb=`find ${tmpd} -iname 'README.md' | wc -l` echo "(3) found ${nb} README.md file(s)" [ "${nb}" != "0" ] && exit 1 ## reinstall to trigger showdiff echo "showdiff" > ${tmpd}/program/a cd ${ddpath} | echo "y" | ${bin} install --showdiff -c ${cfg} --verbose -f [ "$?" != "0" ] && exit 1 # test templated subdir cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF mkdir -p ${tmpd}/nvim mkdir -p ${tmpd}/nvim/dir1 echo "f1" > ${tmpd}/nvim/dir1/file1 mkdir -p ${tmpd}/nvim/dir2 echo "f1" > ${tmpd}/nvim/dir2/file2 echo "ftop" > ${tmpd}/nvim/ftop echo "[+] import top" cd ${ddpath} | ${bin} import -f -c ${cfg} -l link_children -p p1 ${tmpd}/nvim # add sub dir mkdir -p ${tmpd}/nvim/templated echo "noprofile" > ${tmpd}/nvim/templated/ftemplated echo "noprofile" > ${tmpd}/nvim/template echo "[+] import sub" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${tmpd}/nvim/templated cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${tmpd}/nvim/template cfg2="${basedir}/config2.yaml" sed '/d_nvim:/a \ \ \ \ instignore:\n\ \ \ \ - "*template*"' ${cfg} > ${cfg2} cat ${cfg2} ## clean destination files rm -rf ${tmpd}/nvim ## patch template file echo "{{@@ profile @@}}" > ${tmps}/dotfiles/${tmpd}/nvim/templated/ftemplated echo "{{@@ profile @@}}" > ${tmps}/dotfiles/${tmpd}/nvim/template echo "[+] install link_children" cd ${ddpath} | ${bin} install -f -c ${cfg2} -p p1 -V d_nvim [ -d ${tmpd}/nvim/templated ] && echo "templated should not be installed" && exit 1 [ -e ${tmpd}/nvim/templated/ftemplated ] && echo "templated file should not be installed" && exit 1 [ -e ${tmpd}/nvim/template ] && echo "template file should not be installed" && exit 1 echo "[+] install sub" cd ${ddpath} | ${bin} install -f -c ${cfg2} -p p1 -V d_templated echo "[+] install template" cd ${ddpath} | ${bin} install -f -c ${cfg2} -p p1 -V f_template [ ! -d ${tmpd}/nvim/templated ] && echo "templated not installed" && exit 1 [ ! -e ${tmpd}/nvim/templated/ftemplated ] && echo "templated file not installed" && exit 1 [ ! -e ${tmpd}/nvim/template ] && echo "template file not installed" && exit 1 grep 'p1' ${tmpd}/nvim/templated/ftemplated grep 'p1' ${tmpd}/nvim/template echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install-link-children.sh000077500000000000000000000052751436430751200215670ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test link_dotfile_default # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: d_dir1: dst: ${tmpd}/dir1 src: dir1 link: link_children instignore: - '*ignore' profiles: p1: dotfiles: - d_dir1 _EOF #cat ${cfg} # create the dotfile mkdir ${tmps}/dotfiles/dir1 mkdir ${tmps}/dotfiles/dir1/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir1/empty/this.ignore mkdir ${tmps}/dotfiles/dir1/not-empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir1/not-empty/file mkdir ${tmps}/dotfiles/dir1/sub mkdir ${tmps}/dotfiles/dir1/sub/empty echo "{{@@ profile @@}}" > ${tmps}/dotfiles/dir1/sub/empty/that.ignore # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # check normal [ ! -d ${tmpd}/dir1 ] && exit 1 [ -d ${tmpd}/dir1/empty ] && exit 1 [ -d ${tmpd}/dir1/sub ] && exit 1 [ -d ${tmpd}/dir1/sub/empty ] && exit 1 [ ! -d ${tmpd}/dir1/not-empty ] && exit 1 [ ! -e ${tmpd}/dir1/not-empty/file ] && exit 1 # ignored files [ -e ${tmpd}/dir1/empty/this.ignore ] && exit 1 [ -e ${tmpd}/dir1/sub/empty/that.ignore ] && exit 1 cat ${tmpd}/dir1/not-empty/file grep "p1" ${tmpd}/dir1/not-empty/file echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install-negative-ignore.sh000077500000000000000000000067431436430751200221300ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test install negative ignore absolute/relative # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/program/ignore_me echo "some data" > ${tmpd}/program/a echo "some data" > ${tmpd}/program/ignore_me/b echo "some data" > ${tmpd}/program/ignore_me/c # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_program:/a\ \ \ \ \ instignore:\ \ \ \ \ - "*/ignore_me/*"\ \ \ \ \ - "!*/ignore_me/c" ' ${cfg} > ${cfg2} # install rm -rf ${tmpd} echo "[+] install with negative ignore in dotfile" cd ${ddpath} | ${bin} install -c ${cfg2} --verbose [ "$?" != "0" ] && exit 1 echo '(1) expect structure to be . └── program ├── a └── ignore_me └── c' [[ -n "$(find ${tmpd}/program -name a)" ]] || exit 1 echo "(1) found program/a ... good" [[ -n "$(find ${tmpd}/program/ignore_me -name b)" ]] && exit 1 echo "(1) didn't find program/b ... good" [[ -n "$(find ${tmpd}/program/ignore_me -name c)" ]] || exit 1 echo "(1) found program/c ... good" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install-to-temp.sh000077500000000000000000000044541436430751200204270ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test install to temp # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${basedir}/dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: x dst: ${tmpd}/x f_y: src: y dst: ${tmpd}/y link: link f_z: src: z dst: ${tmpd}/z profiles: p1: dotfiles: - f_x - f_y - f_z _EOF echo 'test_x' > ${basedir}/dotfiles/x echo 'test_y' > ${basedir}/dotfiles/y echo "00000000 01 02 03 04 05" | xxd -r - ${basedir}/dotfiles/z echo "[+] install" log="${basedir}/log" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --showdiff --verbose --temp > ${log} tmpfile=`cat ${basedir}/log | grep 'installed to tmp ' | sed 's/^.*to tmp "\(.*\)"./\1/'` echo "tmpfile: ${tmpfile}" clear_on_exit "${tmpfile}" cat ${log} | grep '^3 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/install.sh000077500000000000000000000055571436430751200170510ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test install # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ get_file_mode() { u=`umask` u=`echo ${u} | sed 's/^0*//'` v=$((666 - u)) echo "${v}" } # $1 path # $2 rights has_rights() { echo "testing ${1} is ${2}" [ ! -e "$1" ] && echo "`basename $1` does not exist" && exit 1 local mode=`stat -L -c '%a' "$1"` [ "${mode}" != "$2" ] && echo "bad mode for `basename $1` (${mode} VS expected ${2})" && exit 1 true } # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${basedir}/dotfiles echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" echo "content" > ${basedir}/dotfiles/x # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_x: src: x dst: ${tmpd}/x profiles: p1: dotfiles: - f_x _EOF echo "[+] install" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^1 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/x ] && echo "f_x not installed" && exit 1 # update chmod chmod 666 ${tmpd}/x cd ${ddpath} | ${bin} update -c ${cfg} -f -p p1 --verbose ${tmpd}/x # chmod updated cat ${cfg} | grep "chmod: '666'" chmod 644 ${tmpd}/x mode=`get_file_mode ${tmpd}/x` echo "[+] re-install with no" cd ${ddpath} | printf "N\n" | ${bin} install -c ${cfg} -p p1 --verbose [ "$?" != "0" ] && exit 1 # if user answers N, chmod should not be done has_rights "${tmpd}/x" "${mode}" echo "[+] re-install with yes" cd ${ddpath} | printf "y\n" | ${bin} install -c ${cfg} -p p1 --verbose [ "$?" != "0" ] && exit 1 has_rights "${tmpd}/x" "666" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/jhelpers.sh000077500000000000000000000066031436430751200172100ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test jinja2 helpers from jhelpers # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def profiles: p1: dotfiles: - f_abc - f_def _EOF #cat ${cfg} # create the dotfile echo "this is the test dotfile" > ${tmps}/dotfiles/abc # test exists echo "{%@@ if exists('/dev/null') @@%}" >> ${tmps}/dotfiles/abc echo "this should exist" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc echo "{%@@ if exists('/dev/abcdef') @@%}" >> ${tmps}/dotfiles/abc echo "this should not exist" >> ${tmps}/dotfiles/abc echo "{%@@ endif @@%}" >> ${tmps}/dotfiles/abc # test exists_in_path cat >> ${tmps}/dotfiles/abc << _EOF {%@@ if exists_in_path('cat') @@%} this should exist too {%@@ endif @@%} _EOF cat >> ${tmps}/dotfiles/abc << _EOF {%@@ if exists_in_path('a_name_that_is_unlikely_to_be_chosen_for_an_executable') @@%} this should not exist either {%@@ endif @@%} _EOF #cat ${tmps}/dotfiles/abc echo "this is def" > ${tmps}/dotfiles/def # test basename cat >> ${tmps}/dotfiles/def << _EOF {%@@ set dotfile_filename = basename( _dotfile_abs_dst ) @@%} dotfile dst filename: {{@@ dotfile_filename @@}} _EOF # test dirname cat >> ${tmps}/dotfiles/def << _EOF {%@@ set dotfile_dirname= dirname( _dotfile_abs_dst ) @@%} dotfile dst dirname: {{@@ dotfile_dirname @@}} _EOF #cat ${tmps}/dotfiles/def # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${tmpd}/abc grep '^this should exist' ${tmpd}/abc >/dev/null grep '^this should exist too' ${tmpd}/abc >/dev/null set +e grep '^this should not exist' ${tmpd}/abc >/dev/null && exit 1 grep '^this should not exist either' ${tmpd}/abc >/dev/null && exit 1 set -e #cat ${tmpd}/abc # test def grep "dotfile dst filename: `basename ${tmpd}/def`" ${tmpd}/def grep "dotfile dst dirname: `dirname ${tmpd}/def`" ${tmpd}/def echo "OK" exit 0 dotdrop-1.12.9/tests-ng/key-prefix-sep.sh000077500000000000000000000060771436430751200202510ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test key_prefix and key_separator # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the dotfile mkdir -p ${tmpd}/top touch ${tmpd}/top/.colors mkdir -p ${tmpd}/.mutt/sub touch ${tmpd}/.mutt/sub/colors # create the config file cfg="${tmps}/config.yaml" # normal behavior cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles longkey: true key_prefix: true key_separator: '_' dotfiles: profiles: _EOF # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/top/.colors cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/.mutt/sub cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep -q '_top_colors' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep -q '_mutt_sub' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep '_top_colors' | grep -q 'f_' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep '_mutt_sub' | grep -q 'd_' # pimping rm -rf ${tmps}/* cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles longkey: true key_prefix: false key_separator: '+' dotfiles: profiles: _EOF # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/top/.colors cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/.mutt/sub cat ${cfg} cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep -q '+top+colors' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep -q '+mutt+sub' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep '+top+colors' | grep -qv 'f_' cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -G | cut -f1 -d',' | grep '+mutt+sub' | grep -qv 'd_' echo "OK" exit 0 dotdrop-1.12.9/tests-ng/link-import-default.sh000077500000000000000000000044161436430751200212630ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test the use of the keyword "link_on_import" # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" # create the source echo "abc" > ${tmpd}/abc # import with nolink by default cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: nolink dotfiles: profiles: _EOF # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/abc # checks inside="${tmps}/dotfiles/${tmpd}/abc" [ ! -e ${inside} ] && exit 1 set +e cat ${cfg} | grep 'link:' && exit 1 set -e # import with parent by default cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: link dotfiles: profiles: _EOF # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/abc # checks inside="${tmps}/dotfiles/${tmpd}/abc" [ ! -e ${inside} ] && exit 1 cat ${cfg} cat ${cfg} | grep 'link: absolute' >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/link-templates-dir-home.sh000077500000000000000000000050731436430751200220270ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test link of directory containing templates on home # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d -p ${HOME} --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" # the workdir tmpw=`mktemp -d -p ${HOME} --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" echo "workdir: ${tmpw}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "{{@@ profile @@}}" > ${tmps}/dotfiles/abc/template echo "blabla" >> ${tmps}/dotfiles/abc/template echo "blabla" > ${tmps}/dotfiles/abc/nottemplate # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks [ ! -d ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 #cat ${tmpd}/abc/template #tree -a ${tmpd}/abc/ set +e grep '{{@@' ${tmpd}/abc/template >/dev/null 2>&1 && echo "[ERROR] template in dir not replace" && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/link-templates-dir.sh000077500000000000000000000050351436430751200210770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test link of directory containing templates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" # the workdir tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" echo "workdir: ${tmpw}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "{{@@ profile @@}}" > ${tmps}/dotfiles/abc/template echo "blabla" >> ${tmps}/dotfiles/abc/template echo "blabla" > ${tmps}/dotfiles/abc/nottemplate # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks [ ! -d ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 #cat ${tmpd}/abc/template #tree -a ${tmpd}/abc/ set +e grep '{{@@' ${tmpd}/abc/template >/dev/null 2>&1 && echo "[ERROR] template in dir not replace" && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/link-templates.sh000077500000000000000000000044001436430751200203160ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test link of templates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" # the workdir tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" echo "workdir: ${tmpw}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmps}/dotfiles/abc echo "blabla" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks [ ! -e ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/link-value-tests.sh000077500000000000000000000302401436430751200205750ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test the behavior when playing with link_dotfile_default # and link_on_import on import # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" # ---------------------------------------------------------- echo -e "\n======> import with all default" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=nolink and link_dotfile_default=nolink" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: nolink link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=nolink and link_dotfile_default=nolink and --link=nolink" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: nolink link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V --link=nolink # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=nolink and link_dotfile_default=nolink and --link=link" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: nolink link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V --link=absolute # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:absolute,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ ! -h ${df} ] && echo "not symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=link and link_dotfile_default=nolink" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: link link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:absolute,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ ! -h ${df} ] && echo "not symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=link and link_dotfile_default=nolink and --link=nolink" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: link link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V --link=nolink # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=nolink and link_dotfile_default=link" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: nolink link_dotfile_default: link dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V --link=nolink # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with link_on_import=link and link_dotfile_default=nolink and --link=nolink" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: link link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V --link=nolink # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:nolink,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with all default and --link=link" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} --link=absolute -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "f_`basename ${df}`" | head -1 | grep ',link:absolute,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ ! -h ${df} ] && echo "not a symlink" && exit 1 # ---------------------------------------------------------- echo -e "\n======> import with all default and --link=link_children" # create the source rm -rf ${tmpd}/qwert echo "test" > ${tmpd}/qwert # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import df="${tmpd}/qwert" set +e cd ${ddpath} | ${bin} import -f -c ${cfg} --link=link_children -p p1 ${df} -V [ "$?" = "0" ] && echo "link_children with file should fail" && exit 1 set -e # ---------------------------------------------------------- echo -e "\n======> import with all default and --link=link_children" # create the source rm -rf ${tmpd}/qwert mkdir -p ${tmpd}/qwert echo "test" > ${tmpd}/qwert/file mkdir -p ${tmpd}/qwert/directory echo "test" > ${tmpd}/qwert/directory/file # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} --link=link_children -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "d_`basename ${df}`" | head -1 | grep ',link:link_children,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is a symlink" && exit 1 [ ! -h ${df}/file ] && echo "file is not a symlink" && exit 1 [ ! -h ${df}/directory ] && echo "directory is not a symlink" && exit 1 [ -h ${df}/directory/file ] && echo "directory/file is a symlink" && exit 1 echo -e "\n======> import with link_on_import=link_children and link_dotfile_default=nolink" # create the source rm -rf ${tmpd}/qwert mkdir -p ${tmpd}/qwert echo "test" > ${tmpd}/qwert/file mkdir -p ${tmpd}/qwert/directory echo "test" > ${tmpd}/qwert/directory/file # clean rm -rf ${tmps}/dotfiles mkdir -p ${tmps}/dotfiles # config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_on_import: link_children link_dotfile_default: nolink dotfiles: profiles: _EOF # import df="${tmpd}/qwert" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 ${df} -V # checks cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} files -c ${cfg} -p p1 -V -G | grep "d_`basename ${df}`" | head -1 | grep ',link:link_children,' # try to install rm -rf ${tmpd}/qwert cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V [ ! -e ${df} ] && echo "does not exist" && exit 1 [ -h ${df} ] && echo "is a symlink" && exit 1 [ ! -h ${df}/file ] && echo "file is not a symlink" && exit 1 [ ! -h ${df}/directory ] && echo "directory is not a symlink" && exit 1 [ -h ${df}/directory/file ] && echo "directory/file is a symlink" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/macro-with-globals.sh000077500000000000000000000042371436430751200210700ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # import variables from file # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p0: dotfiles: - f_abc variables: global: global_var local: local_var _EOF # create the source mkdir -p ${tmps}/dotfiles/ cat > ${tmps}/dotfiles/macro_file << _EOF {%@@ macro macro(var) @@%} {{@@ global @@}} {{@@ var @@}} {%@@ endmacro @@%} _EOF cat > ${tmps}/dotfiles/abc << _EOF {%@@ from 'macro_file' import macro with context @@%} {{@@ macro(local) @@}} _EOF # install cd ${ddpath} | ${bin} install -c ${cfg} -p p0 -V -f # test file content cat ${tmpd}/abc grep 'global_var' ${tmpd}/abc >/dev/null 2>&1 grep 'local_var' ${tmpd}/abc >/dev/null 2>&1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/minversion.sh000077500000000000000000000051271436430751200175650ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test minversion # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF # create the source mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc ln -s ${tmps}/dotfiles/abc ${tmpd}/abc # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V # ensure minversion is present cat ${cfg} grep 'link: absolute' ${cfg} grep 'minversion' ${cfg} # fake a higher version cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles minversion: 100.1.2 dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V [ "$?" != "1" ] && echo "minversion not working" && exit 1 set -e # all clean cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF # compare cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V # test cat ${cfg} grep 'minversion' ${cfg} && echo "minversion added, not needed" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/negative-ignore-no-match.sh000077500000000000000000000070421436430751200221610ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test that dotdrop warns when a negative ignore pattern # does not match a file that would be ignored # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # some files mkdir -p ${tmpd}/program/ignore_me echo "some data" > ${tmpd}/program/a echo "some data" > ${tmpd}/program/ignore_me/b echo "some data" > ${tmpd}/program/ignore_me/c # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/program # adding ignore in dotfile cfg2="${basedir}/config2.yaml" sed '/d_program:/a\ \ \ \ \ instignore:\ \ \ \ \ - "!*/ignore_me/c" ' ${cfg} > ${cfg2} # install rm -rf ${tmpd} echo "[+] install with negative ignore in dotfile" echo '(1) expect dotdrop install to warn when negative ignore pattern does not match an already-ignored file' patt="[WARN] no files that are currently being ignored match \"*/ignore_me/c\". In order for a negative ignore pattern to work, it must match a file that is being ignored by a previous ignore pattern." cd ${ddpath} | ${bin} install -c ${cfg2} --verbose 2>&1 >/dev/null | grep -F "${patt}" || (echo "dotdrop did not warn when negative ignore pattern did not match an already-ignored file" && exit 1) echo "OK" exit 0 dotdrop-1.12.9/tests-ng/notemplate.sh000077500000000000000000000173351436430751200175500ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test notemplate # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" # globally cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles template_dotfile_default: false dotfiles: f_f1: dst: ${tmpd}/f1 src: f1 d_d1: dst: ${tmpd}/dir1 src: dir1 d_d2: dst: ${tmpd}/dir2 src: dir2 link: link d_d3: dst: ${tmpd}/dir3 src: dir3 link: link_children f_fl: dst: ${tmpd}/fl src: fl link: link f_fn: dst: ${tmpd}/fn src: fn template: true profiles: p1: dotfiles: - f_f1 - d_d1 - d_d2 - d_d3 - f_fl - f_fn _EOF #cat ${cfg} # create the dotfile echo "before" > ${tmps}/dotfiles/f1 echo "{#@@ should not be stripped @@#}" >> ${tmps}/dotfiles/f1 echo "{{@@ header() @@}}" >> ${tmps}/dotfiles/f1 echo "after" >> ${tmps}/dotfiles/f1 # create the directory mkdir -p ${tmps}/dotfiles/dir1/d1 echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir1/d1/f2 # create the linked directory mkdir -p ${tmps}/dotfiles/dir2/d1 echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir2/d1/f2 # create the link_children directory mkdir -p ${tmps}/dotfiles/dir3/{s1,s2,s3} echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir3/s1/f1 echo "{{@@ header() @@}}" > ${tmps}/dotfiles/dir3/s2/f2 # create the linked dotfile echo "{{@@ header() @@}}" > ${tmps}/dotfiles/fl # create the normal dotfile echo "before" > ${tmps}/dotfiles/fn echo "{#@@ should be stripped @@#}" >> ${tmps}/dotfiles/fn echo "after" >> ${tmps}/dotfiles/fn # install echo "installing" cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V echo "comparing" cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V # simple file echo "doing globally" echo "* test simple file" [ ! -e ${tmpd}/f1 ] && echo 'not installed1' && exit 1 grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1) grep 'should not be stripped' ${tmpd}/f1 || (echo "comment stripped" && exit 1) # directory echo "* test directory" [ ! -d ${tmpd}/dir1 ] && echo 'not installed1' && exit 1 [ ! -d ${tmpd}/dir1/d1 ] && echo 'not installed2' && exit 1 [ ! -e ${tmpd}/dir1/d1/f2 ] && echo 'not installed3' && exit 1 grep 'header' ${tmpd}/dir1/d1/f2 || (echo "header stripped" && exit 1) # linked directory echo "* test linked directory" [ ! -h ${tmpd}/dir2 ] && echo 'not installed1' && exit 1 [ ! -d ${tmpd}/dir2/d1 ] && echo 'not installed2' && exit 1 [ ! -e ${tmpd}/dir2/d1/f2 ] && echo 'not installed3' && exit 1 grep 'header' ${tmpd}/dir2/d1/f2 || (echo "header stripped" && exit 1) # children_link directory echo "* test link_children directory" [ ! -d ${tmpd}/dir3 ] && echo 'not installed1' && exit 1 [ ! -h ${tmpd}/dir3/s1 ] && echo 'not installed2' && exit 1 [ ! -h ${tmpd}/dir3/s2 ] && echo 'not installed3' && exit 1 [ ! -h ${tmpd}/dir3/s3 ] && echo 'not installed4' && exit 1 [ ! -e ${tmpd}/dir3/s1/f1 ] && echo 'not installed5' && exit 1 [ ! -e ${tmpd}/dir3/s2/f2 ] && echo 'not installed6' && exit 1 grep 'header' ${tmpd}/dir3/s1/f1 || (echo "header stripped" && exit 1) grep 'header' ${tmpd}/dir3/s2/f2 || (echo "header stripped" && exit 1) # linked file echo "* test linked file" [ ! -h ${tmpd}/fl ] && echo 'not installed' && exit 1 grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1) # normal dotfile echo "* normal dotfile" [ ! -e ${tmpd}/fn ] && echo 'not installed' && exit 1 grep 'should be stripped' ${tmpd}/fn && echo "not templated" && exit 1 # test backup done echo "before" > ${tmps}/dotfiles/f1 cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V [ ! -e ${tmpd}/f1.dotdropbak ] && echo "backup not done" && exit 1 # re-create the dotfile echo "before" > ${tmps}/dotfiles/f1 echo "{#@@ should not be stripped @@#}" >> ${tmps}/dotfiles/f1 echo "{{@@ header() @@}}" >> ${tmps}/dotfiles/f1 echo "after" >> ${tmps}/dotfiles/f1 # through the dotfile cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles template_dotfile_default: true dotfiles: f_f1: dst: ${tmpd}/f1 src: f1 template: false d_d1: dst: ${tmpd}/dir1 src: dir1 template: false d_d2: dst: ${tmpd}/dir2 src: dir2 link: link template: false d_d3: dst: ${tmpd}/dir3 src: dir3 link: link_children template: false f_fl: dst: ${tmpd}/fl src: fl link: link template: false f_fn: dst: ${tmpd}/fn src: fn profiles: p1: dotfiles: - f_f1 - d_d1 - d_d2 - d_d3 - f_fl - f_fn _EOF #cat ${cfg} # clean destination rm -rf ${tmpd}/* # install cd ${ddpath} | ${bin} install -f --showdiff -c ${cfg} -p p1 -V cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -V # simple file echo "doing specifically" echo "* test simple file" [ ! -e ${tmpd}/f1 ] && echo 'not installed1' && exit 1 grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1) grep 'should not be stripped' ${tmpd}/f1 || (echo "comment stripped" && exit 1) # directory echo "* test directory" [ ! -d ${tmpd}/dir1 ] && echo 'not installed1' && exit 1 [ ! -d ${tmpd}/dir1/d1 ] && echo 'not installed2' && exit 1 [ ! -e ${tmpd}/dir1/d1/f2 ] && echo 'not installed3' && exit 1 grep 'header' ${tmpd}/dir1/d1/f2 || (echo "header stripped" && exit 1) # linked directory echo "* test linked directory" [ ! -h ${tmpd}/dir2 ] && echo 'not installed1' && exit 1 [ ! -d ${tmpd}/dir2/d1 ] && echo 'not installed2' && exit 1 [ ! -e ${tmpd}/dir2/d1/f2 ] && echo 'not installed3' && exit 1 grep 'header' ${tmpd}/dir2/d1/f2 || (echo "header stripped" && exit 1) # children_link directory echo "* test link_children directory" [ ! -d ${tmpd}/dir3 ] && echo 'not installed1' && exit 1 [ ! -h ${tmpd}/dir3/s1 ] && echo 'not installed2' && exit 1 [ ! -h ${tmpd}/dir3/s2 ] && echo 'not installed3' && exit 1 [ ! -h ${tmpd}/dir3/s3 ] && echo 'not installed4' && exit 1 [ ! -e ${tmpd}/dir3/s1/f1 ] && echo 'not installed5' && exit 1 [ ! -e ${tmpd}/dir3/s2/f2 ] && echo 'not installed6' && exit 1 grep 'header' ${tmpd}/dir3/s1/f1 || (echo "header stripped" && exit 1) grep 'header' ${tmpd}/dir3/s2/f2 || (echo "header stripped" && exit 1) # linked file echo "* test linked file" [ ! -h ${tmpd}/fl ] && echo 'not installed' && exit 1 grep 'header' ${tmpd}/f1 || (echo "header stripped" && exit 1) # normal dotfile echo "* normal dotfile" [ ! -e ${tmpd}/fn ] && echo 'not installed' && exit 1 grep 'should not be stripped' ${tmpd}/fn && echo "no templated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/profile-actions.sh000077500000000000000000000066601436430751200204750ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test actions per profile # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" # the action temp tmpa=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpa}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles actions: pre: preaction: echo 'pre' >> ${tmpa}/pre preaction2: echo 'pre2' >> ${tmpa}/pre2 post: postaction: echo 'post' >> ${tmpa}/post postaction2: echo 'post2' >> ${tmpa}/post2 nakedaction: echo 'naked' >> ${tmpa}/naked dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def f_ghi: dst: ${tmpd}/ghi src: ghi profiles: p0: actions: - preaction2 - postaction2 - nakedaction dotfiles: - f_abc - f_def - f_ghi _EOF #cat ${cfg} # list profiles cd ${ddpath} | ${bin} profiles -c ${cfg} -V # create the dotfile echo "test" > ${tmps}/dotfiles/abc echo "test" > ${tmps}/dotfiles/def echo "test" > ${tmps}/dotfiles/ghi # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 -V # check actions executed [ ! -e ${tmpa}/pre2 ] && echo 'action not executed' && exit 1 [ ! -e ${tmpa}/post2 ] && echo 'action not executed' && exit 1 [ ! -e ${tmpa}/naked ] && echo 'action not executed' && exit 1 grep pre2 ${tmpa}/pre2 nb=`wc -l ${tmpa}/pre2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "profile action executed multiple times" && exit 1 grep post2 ${tmpa}/post2 nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" != "1" ] && echo "profile action executed multiple times" && exit 1 grep naked ${tmpa}/naked nb=`wc -l ${tmpa}/naked | awk '{print $1}'` [ "${nb}" != "1" ] && echo "profile action executed multiple times" && exit 1 # install again cd ${ddpath} | ${bin} install -f -c ${cfg} -p p0 -V # check actions not executed twice nb=`wc -l ${tmpa}/post2 | awk '{print $1}'` [ "${nb}" -gt "1" ] && echo "action post2 executed twice" && exit 1 nb=`wc -l ${tmpa}/naked | awk '{print $1}'` [ "${nb}" -gt "1" ] && echo "action naked executed twice" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/profile-dyninclude.sh000077500000000000000000000065541436430751200211750ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test profile dynvariables and included dynvariables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cfg2="${tmps}/sub.yaml" cat > ${cfg} << _EOF config: dotpath: dotfiles import_configs: - sub.yaml variables: mainvar: 'not-that' subvar: 'not-that-either' dynvariables: maindyn: 'echo wont-work' subdyn: 'echo wont-work-either' dotfiles: profiles: profile_1: include: - subprofile dynvariables: maindyn: 'echo maindyncontent' variables: mainvar: 'maincontent' profile_2: include: - subignore _EOF #cat ${cfg} cat > ${cfg2} << _EOF config: dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def f_ghi: dst: '${tmpd}/{{@@ ghi @@}}' src: ghi variables: mainvar: 'bad0' subvar: 'bad1' dynvariables: maindyn: 'echo bad2' subdyn: 'echo bad3' profiles: subprofile: dotfiles: - f_abc - f_ghi dynvariables: subdyn: 'echo subdyncontent' ghi: 'echo ghi' variables: subvar: 'subcontent' subignore: dotfiles: - f_def _EOF #cat ${cfg2} # create the dotfile echo "start" > ${tmps}/dotfiles/abc echo "{{@@ mainvar @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ maindyn @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ subdyn @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ subvar @@}}" >> ${tmps}/dotfiles/abc echo "end" >> ${tmps}/dotfiles/abc #cat ${tmps}/dotfiles/abc echo "ghi content" > ${tmps}/dotfiles/ghi # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p profile_1 --verbose # check dotfile exists [ ! -e ${tmpd}/abc ] && exit 1 grep 'maincontent' ${tmpd}/abc >/dev/null || (echo "variables 1 not resolved" && exit 1) grep 'maindyncontent' ${tmpd}/abc >/dev/null || (echo "dynvariables 1 not resolved" && exit 1) grep 'subcontent' ${tmpd}/abc >/dev/null || (echo "variables 2 not resolved" && exit 1) grep 'subdyncontent' ${tmpd}/abc >/dev/null || (echo "dynvariables 2 not resolved" && exit 1) #cat ${tmpd}/abc [ ! -e ${tmpd}/ghi ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/profile-dynvariables.sh000077500000000000000000000064131436430751200215140ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test variables per profile # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create a shell script export TESTENV="this is my global testenv" scr=`mktemp --suffix='-dotdrop-tests' || mktemp -d` chmod +x ${scr} echo -e "#!/bin/bash\necho $TESTENV\n" >> ${scr} export TESTENV2="this is my profile testenv" scr2=`mktemp --suffix='-dotdrop-tests' || mktemp -d` chmod +x ${scr2} echo -e "#!/bin/bash\necho $TESTENV2\n" >> ${scr2} clear_on_exit "${scr}" clear_on_exit "${scr2}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: gvar1: "global1" gvar2: "global2" dynvariables: gdvar1: head -1 ${cur}/helpers gdvar2: "echo 'this is some test' | rev | tr ' ' ','" gdvar3: ${scr} dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: variables: gvar1: "local1" lvar1: "local2" dynvariables: gdvar3: ${scr2} pdvar1: "echo 'abc' | rev" dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "===================" > ${tmps}/dotfiles/abc echo "{{@@ gvar1 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ gvar2 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ gdvar1 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ gdvar2 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ gdvar3 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ lvar1 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ pdvar1 @@}}" >> ${tmps}/dotfiles/abc echo "===================" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V cat ${tmpd}/abc # test variables grep '^local1' ${tmpd}/abc >/dev/null grep '^global2' ${tmpd}/abc >/dev/null grep '^local2' ${tmpd}/abc >/dev/null # test dynvariables grep "^# author: deadc0de6" ${tmpd}/abc >/dev/null grep '^tset,emos,si,siht' ${tmpd}/abc >/dev/null grep "^${TESTENV2}" ${tmpd}/abc > /dev/null grep "^cba" ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/profile-import-dotfiles.sh000077500000000000000000000046401436430751200221520ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test dotfiles imported in profile # and importing # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cfg2="${tmps}/dotfiles.yaml" src="dotdrop-test" dst=".dotdrop-test" clear_on_exit "${HOME}/${dst}" cat > ${cfg} << _EOF config: dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ~/${dst} src: ${src} profiles: p1: import: - dotfiles.yaml dotfiles: - f_abc _EOF cat ${cfg} cat > ${cfg2} << _EOF dotfiles: - f_def _EOF #cat ${cfg2} # create the dotfile echo "abc" > ${tmps}/dotfiles/abc echo "abc" > ${tmpd}/abc echo "def" > ${tmps}/dotfiles/${src} echo "def" > ${HOME}/${dst} # import ## this is a special case since the dotfile must ## be in home (because it is strip) echo ${ddpath} echo ${bin} cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 --verbose ~/${dst} cat ${cfg} echo '----------' cat ${cfg2} cnt=$(cd ${ddpath} | ${bin} files -G -c ${cfg} -p p1 | grep '^f_def' | wc -l) [ "${cnt}" != "1" ] && echo "imported twice! (${cnt})" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/profile-undefined-variables.sh000077500000000000000000000055521436430751200227430ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test variables defined in a different profile # than the one selected # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: "${tmpd}/{{@@ defined_in_main @@}}" src: abc f_def: dst: "${tmpd}/{{@@ defined_in_alt @@}}" src: def profiles: pmain: dynvariables: defined_in_main: echo abc dotfiles: - f_abc palt: dynvariables: defined_in_alt: echo def dotfiles: - f_def pall: dynvariables: defined_in_main: echo abcall defined_in_alt: echo defall dotfiles: - ALL pinclude: include: - pmain _EOF #cat ${cfg} # create the dotfile echo "main" > ${tmps}/dotfiles/abc echo "alt" > ${tmps}/dotfiles/def # install pmain echo "install pmain" cd ${ddpath} | ${bin} install -f -c ${cfg} -p pmain -V [ ! -e ${tmpd}/abc ] && echo "dotfile not installed" && exit 1 grep main ${tmpd}/abc # install pall echo "install pall" cd ${ddpath} | ${bin} install -f -c ${cfg} -p pall -V [ ! -e ${tmpd}/abcall ] && echo "dotfile not installed" && exit 1 grep main ${tmpd}/abcall [ ! -e ${tmpd}/defall ] && echo "dotfile not installed" && exit 1 grep alt ${tmpd}/defall # install pinclude echo "install pinclude" rm -f ${tmpd}/abc cd ${ddpath} | ${bin} install -f -c ${cfg} -p pinclude -V [ ! -e ${tmpd}/abc ] && echo "dotfile not installed" && exit 1 grep main ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/re-import.sh000077500000000000000000000106641436430751200173140ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test re-importing file # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "~/.dotdrop-test" # create the dotfile echo "original" > ${tmpd}/testfile # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: profiles: _EOF #cat ${cfg} # import echo "[+] import file" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile cat ${cfg} # ensure exists and is not link [ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1 cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1 grep 'original' ${tmps}/dotfiles/${tmpd}/testfile nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l` [ "${nb}" != "1" ] && echo 'not 1 entry' && exit 1 # re-import without changing echo "[+] re-import without changes" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile cat ${cfg} # test is only once [ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1 cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1 grep 'original' ${tmps}/dotfiles/${tmpd}/testfile nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l` [ "${nb}" != "1" ] && echo 'two entries!' && exit 1 # re-import with changes echo "[+] re-import with changes" echo 'modified' > ${tmpd}/testfile cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ${tmpd}/testfile cat ${cfg} # test is only once [ ! -e ${tmps}/dotfiles/${tmpd}/testfile ] && echo "does not exist" && exit 1 cat ${cfg} | grep ${tmpd}/testfile >/dev/null 2>&1 grep 'modified' ${tmps}/dotfiles/${tmpd}/testfile nb=`cat ${cfg} | grep ${tmpd}/testfile | wc -l` [ "${nb}" != "1" ] && echo 'two entries!' && exit 1 # ################################################### echo 'original' > "${HOME}/.dotdrop.test" # import in home echo "[+] import file in home" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test cat ${cfg} # ensure exists and is not link [ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1 cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1 grep 'original' ${tmps}/dotfiles/dotdrop.test nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l` [ "${nb}" != "1" ] && echo 'not 1 entry' && exit 1 # re-import without changing echo "[+] re-import without changes in home" cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test cat ${cfg} # test is only once [ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1 cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1 grep 'original' ${tmps}/dotfiles/dotdrop.test nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l` [ "${nb}" != "1" ] && echo 'two entries!' && exit 1 # re-import with changes echo "[+] re-import with changes in home" echo 'modified' > ~/.dotdrop.test cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -V ~/.dotdrop.test cat ${cfg} # test is only once [ ! -e "${tmps}/dotfiles/dotdrop.test" ] && echo "does not exist" && exit 1 cat ${cfg} | grep "~/.dotdrop.test" >/dev/null 2>&1 grep 'modified' ${tmps}/dotfiles/dotdrop.test nb=`cat ${cfg} | grep "~/.dotdrop.test" | wc -l` [ "${nb}" != "1" ] && echo 'two entries!' && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/recinclude.sh000077500000000000000000000053711436430751200175120ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test recursive include # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def profiles: host: include: - user common: dotfiles: - f_def user: dotfiles: - f_abc include: - common _EOF # create the source mkdir -p ${tmps}/dotfiles/ content_abc="testrecinclude_abc" echo "${content_abc}" > ${tmps}/dotfiles/abc content_def="testrecinclude_def" echo "${content_def}" > ${tmps}/dotfiles/def # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p host -V # checks [ ! -e ${tmpd}/abc ] && echo "abc not installed" && exit 1 echo "abc installed" grep ${content_abc} ${tmpd}/abc [ ! -e ${tmpd}/def ] && echo "def not installed" && exit 1 echo "def installed" grep ${content_def} ${tmpd}/def # test cyclic include cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def profiles: host: include: - user common: include: - host dotfiles: - f_def user: dotfiles: - f_abc include: - common _EOF # install set +e cd ${ddpath} | ${bin} install -f -c ${cfg} -p host -V [ "$?" = 0 ] && exit 1 set -e echo "OK" exit 0 dotdrop-1.12.9/tests-ng/recvariables.sh000077500000000000000000000050151436430751200200320ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test recursive variables # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: var1: "var1" var2: "{{@@ var1 @@}} var2" var3: "{{@@ var2 @@}} var3" var4: "{{@@ dvar4 @@}}" dynvariables: dvar1: "echo dvar1" dvar2: "{{@@ dvar1 @@}} dvar2" dvar3: "{{@@ dvar2 @@}} dvar3" dvar4: "echo {{@@ var3 @@}}" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "var3: {{@@ var3 @@}}" > ${tmps}/dotfiles/abc echo "dvar3: {{@@ dvar3 @@}}" >> ${tmps}/dotfiles/abc echo "var4: {{@@ var4 @@}}" >> ${tmps}/dotfiles/abc echo "dvar4: {{@@ dvar4 @@}}" >> ${tmps}/dotfiles/abc #cat ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${tmpd}/abc grep '^var3: var1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar3: dvar1 dvar2 dvar3' ${tmpd}/abc >/dev/null grep '^var4: echo var1 var2 var3' ${tmpd}/abc >/dev/null grep '^dvar4: var1 var2 var3' ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/remove.sh000077500000000000000000000100271436430751200166640ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test remove # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def f_last: dst: ${tmpd}/last src: last profiles: p1: dotfiles: - f_abc - f_def p2: dotfiles: - f_def last: dotfiles: - f_last _EOF cfgbak="${tmps}/config.yaml.bak" cp ${cfg} ${cfgbak} # create the dotfile echo "abc" > ${tmps}/dotfiles/abc echo "abc" > ${tmpd}/abc echo "def" > ${tmps}/dotfiles/def echo "def" > ${tmpd}/def # remove with bad profile cd ${ddpath} | ${bin} remove -f -k -p empty -c ${cfg} f_abc -V [ ! -e ${tmps}/dotfiles/abc ] && echo "dotfile in dotpath deleted" && exit 1 [ ! -e ${tmpd}/abc ] && echo "source dotfile deleted" && exit 1 [ ! -e ${tmps}/dotfiles/def ] && echo "dotfile in dotpath deleted" && exit 1 [ ! -e ${tmpd}/def ] && echo "source dotfile deleted" && exit 1 # ensure config not altered diff ${cfg} ${cfgbak} # remove by key echo "[+] remove f_abc by key" cd ${ddpath} | ${bin} remove -p p1 -f -k -c ${cfg} f_abc -V cat ${cfg} echo "[+] remove f_def by key" cd ${ddpath} | ${bin} remove -p p2 -f -k -c ${cfg} f_def -V cat ${cfg} # checks [ -e ${tmps}/dotfiles/abc ] && echo "dotfile in dotpath not deleted" && exit 1 [ ! -e ${tmpd}/abc ] && echo "source dotfile deleted" && exit 1 [ -e ${tmps}/dotfiles/def ] && echo "dotfile in dotpath not deleted" && exit 1 [ ! -e ${tmpd}/def ] && echo "source dotfile deleted" && exit 1 echo "[+] =========" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: ${tmpd}/def src: def f_last: dst: ${tmpd}/last src: last profiles: p1: dotfiles: - f_abc - f_def p2: dotfiles: - f_def last: dotfiles: - f_last _EOF cat ${cfg} # create the dotfile echo "abc" > ${tmps}/dotfiles/abc echo "abc" > ${tmpd}/abc echo "def" > ${tmps}/dotfiles/def echo "def" > ${tmpd}/def # remove by key echo "[+] remove f_abc by path" cd ${ddpath} | ${bin} remove -p p1 -f -c ${cfg} ${tmpd}/abc -V cat ${cfg} echo "[+] remove f_def by path" cd ${ddpath} | ${bin} remove -p p2 -f -c ${cfg} ${tmpd}/def -V cat ${cfg} # checks [ -e ${tmps}/dotfiles/abc ] && echo "(2) dotfile in dotpath not deleted" && exit 1 [ ! -e ${tmpd}/abc ] && echo "(2) source dotfile deleted" && exit 1 [ -e ${tmps}/dotfiles/def ] && echo "(2) dotfile in dotpath not deleted" && exit 1 [ ! -e ${tmpd}/def ] && echo "(2) source dotfile deleted" && exit 1 cat ${cfg} echo "OK" exit 0 dotdrop-1.12.9/tests-ng/symlink-relative.sh000077500000000000000000000116751436430751200207000ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test relative symlink # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" ################################################## # test symlink directory ################################################## # create the file echo "file1" > ${tmps}/dotfiles/abc mkdir -p ${tmps}/dotfiles/def echo 'file2' > ${tmps}/dotfiles/def/afile echo '{{@@ header() @@}}' > ${tmps}/dotfiles/ghi mkdir -p ${tmps}/dotfiles/jkl echo '{{@@ header() @@}}' > ${tmps}/dotfiles/jkl/anotherfile # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: relative f_abc2: dst: ${tmpd}/abc2 src: abc link: absolute d_def: dst: ${tmpd}/def src: def link: relative f_ghi: dst: ${tmpd}/ghi src: ghi link: relative d_jkl: dst: ${tmpd}/jkl src: jkl link: relative profiles: p1: dotfiles: - f_abc - f_abc2 - d_def - f_ghi - d_jkl _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # ensure exists and is link [ ! -h ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/abc2 ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/def ] && echo "not a symlink" && exit 1 [ ! -d ${tmpd}/def ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/ghi ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/jkl ] && echo "not a symlink" && exit 1 [ ! -d ${tmpd}/jkl ] && echo "not a symlink" && exit 1 ls -l ${tmpd}/abc | grep '\.\.' || exit 1 ls -l ${tmpd}/abc2 ls -l ${tmpd}/def | grep '\.\.' || exit 1 ls -l ${tmpd}/ghi | grep '\.\.' || exit 1 ls -l ${tmpd}/jkl | grep '\.\.' || exit 1 grep 'file1' ${tmpd}/abc grep 'file1' ${tmpd}/abc2 grep 'file2' ${tmpd}/def/afile grep 'This dotfile is managed using dotdrop' ${tmpd}/ghi grep 'This dotfile is managed using dotdrop' ${tmpd}/jkl/anotherfile [[ $(realpath --relative-base="${tmpw}" -- "$(realpath ${tmpd}/ghi)") =~ "^/" ]] && echo "ghi not subpath of workdir" && exit 1 [[ $(realpath --relative-base="${tmpw}" -- "$(realpath ${tmpd}/jkl)") =~ ^/ ]] && echo "jkl not subpath of workdir" && exit 1 ############################################################################################################################# rm -rf ${tmps} ${tmpd} ${tmpw} # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles tmpd="${tmps}" mkdir -p ${tmpd} clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the file echo "file1" > ${tmps}/dotfiles/abc mkdir -p ${tmps}/dotfiles/def echo 'file2' > ${tmps}/dotfiles/def/afile # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: relative f_abc2: dst: ${tmpd}/abc2 src: abc link: absolute d_def: dst: ${tmpd}/def src: def link: relative profiles: p1: dotfiles: - f_abc - f_abc2 - d_def _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V # ensure exists and is link [ ! -h ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/abc2 ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/def ] && echo "not a symlink" && exit 1 [ ! -d ${tmpd}/def ] && echo "not a symlink" && exit 1 grep 'file1' ${tmpd}/abc grep 'file1' ${tmpd}/abc2 grep 'file2' ${tmpd}/def/afile echo "OK" exit 0 dotdrop-1.12.9/tests-ng/symlink.sh000077500000000000000000000115131436430751200170560ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test symlinking dotfiles # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" ################################################## # test symlink directory ################################################## # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "file1" > ${tmps}/dotfiles/abc/file1 echo "file2" > ${tmps}/dotfiles/abc/file2 # create a shell script # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: d_abc: dst: ${tmpd}/abc src: abc link: link profiles: p1: dotfiles: - d_abc _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and is link [ ! -h ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -e ${tmpd}/abc/file1 ] && echo "does not exist" && exit 1 [ ! -e ${tmpd}/abc/file2 ] && echo "does not exist" && exit 1 ################################################## # test symlink files ################################################## # clean rm -rf ${tmps}/dotfiles ${tmpd}/abc # create the dotfiles mkdir -p ${tmps}/dotfiles/ echo "abc" > ${tmps}/dotfiles/abc # create a shell script # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: link profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and is link [ ! -h ${tmpd}/abc ] && echo "not a symlink" && exit 1 ################################################## # test link_children ################################################## # clean rm -rf ${tmps}/dotfiles ${tmpd}/abc # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "file1" > ${tmps}/dotfiles/abc/file1 echo "file2" > ${tmps}/dotfiles/abc/file2 # create a shell script # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: d_abc: dst: ${tmpd}/abc src: abc link: link_children profiles: p1: dotfiles: - d_abc _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and is link [ ! -d ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/abc/file1 ] && echo "does not exist" && exit 1 [ ! -h ${tmpd}/abc/file2 ] && echo "does not exist" && exit 1 ################################################## # test link_children with templates ################################################## # clean rm -rf ${tmps}/dotfiles ${tmpd}/abc # create the dotfile mkdir -p ${tmps}/dotfiles/abc echo "{{@@ profile @@}}" > ${tmps}/dotfiles/abc/file1 echo "file2" > ${tmps}/dotfiles/abc/file2 # create a shell script # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles link_dotfile_default: nolink dotfiles: d_abc: dst: ${tmpd}/abc src: abc link: link_children profiles: p1: dotfiles: - d_abc _EOF #cat ${cfg} # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V #cat ${cfg} # ensure exists and is link [ ! -d ${tmpd}/abc ] && echo "not a symlink" && exit 1 [ ! -h ${tmpd}/abc/file1 ] && echo "does not exist" && exit 1 [ ! -h ${tmpd}/abc/file2 ] && echo "does not exist" && exit 1 grep '^p1$' ${tmpd}/abc/file1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/template-dotpath.sh000077500000000000000000000050001436430751200206360ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test dotpath templated # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ dotpath="xyz" # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/${dotpath} echo "[+] dotdrop dir: ${tmps}" echo "[+] dotpath dir: ${tmps}/${dotpath}" # dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" echo "content" > ${tmps}/${dotpath}/abc # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: "{{@@ env['DOTDROP_DOTPATH'] @@}}" dotfiles: f_abc: src: abc dst: ${tmpd}/abc profiles: p1: dotfiles: - f_abc _EOF echo "[+] install" export DOTDROP_DOTPATH=${dotpath} cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^1 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/abc ] && echo "f_abc not installed" && exit 1 # clean rm ${tmpd}/abc # create the config file cat > ${cfg} << _EOF config: backup: true create: true dotpath: "{{@@ var1 @@}}" variables: var1: "${dotpath}" dotfiles: f_abc: src: abc dst: ${tmpd}/abc profiles: p1: dotfiles: - f_abc _EOF echo "[+] install" cd ${ddpath} | ${bin} install -c ${cfg} -f -p p1 --verbose | grep '^1 dotfile(s) installed.$' [ "$?" != "0" ] && exit 1 [ ! -e ${tmpd}/abc ] && echo "f_abc not installed" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/template-link-value.sh000077500000000000000000000075201436430751200212530ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2020, deadc0de6 # # test tmeplate link value # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: link_true: "link" link_false: "nolink" link_children_val: "link_children" dynvariables: dyn_link_true: "echo link" dyn_link_false: "echo nolink" dyn_link_children_val: "echo link_children" dotfiles: f_a: dst: ${tmpd}/a src: a link: "{{@@ link_false @@}}" f_b: dst: ${tmpd}/b src: b link: "{{@@ link_true @@}}" f_c: dst: ${tmpd}/c src: c link: "{{@@ dyn_link_false @@}}" f_d: dst: ${tmpd}/d src: d link: "{{@@ dyn_link_true @@}}" f_e: dst: ${tmpd}/e src: e link: "{{@@ link_children_val @@}}" f_f: dst: ${tmpd}/f src: f link: "{{@@ dyn_link_children_val @@}}" f_not: dst: ${tmpd}/n src: n link: "{{@@ link_true @@}}" profiles: p1: dotfiles: - f_a - f_b - f_c - f_d - f_e - f_f _EOF #cat ${cfg} # create the dotfile echo "filea" > ${tmps}/dotfiles/a echo "fileb" > ${tmps}/dotfiles/b echo "filec" > ${tmps}/dotfiles/c echo "filed" > ${tmps}/dotfiles/d mkdir -p ${tmps}/dotfiles/e/{1,2,3} echo filee > ${tmps}/dotfiles/e/1/file echo filee > ${tmps}/dotfiles/e/2/file echo filee > ${tmps}/dotfiles/e/3/file mkdir -p ${tmps}/dotfiles/f/{1,2,3} echo filee > ${tmps}/dotfiles/f/1/file echo filee > ${tmps}/dotfiles/f/2/file echo filee > ${tmps}/dotfiles/f/3/file # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks [ ! -e ${tmpd}/a ] && echo "[ERROR] dotfile a not linked" && exit 1 [ ! -h ${tmpd}/b ] && echo "[ERROR] dotfile b linked" && exit 1 [ ! -e ${tmpd}/c ] && echo "[ERROR] dotfile c not linked" && exit 1 [ ! -h ${tmpd}/d ] && echo "[ERROR] dotfile d linked" && exit 1 # link_children [ ! -d ${tmpd}/e ] && echo "[ERROR] dir e does not exist" && exit 1 [ ! -h ${tmpd}/e/1 ] && echo "[ERROR] children e/1 not linked" && exit 1 [ ! -h ${tmpd}/e/2 ] && echo "[ERROR] children e/2 not linked" && exit 1 [ ! -h ${tmpd}/e/3 ] && echo "[ERROR] children e/3 not linked" && exit 1 [ ! -d ${tmpd}/f ] && echo "[ERROR] dir f does not exist" && exit 1 [ ! -h ${tmpd}/f/1 ] && echo "[ERROR] children f/1 not linked" && exit 1 [ ! -h ${tmpd}/f/2 ] && echo "[ERROR] children f/2 not linked" && exit 1 [ ! -h ${tmpd}/f/3 ] && echo "[ERROR] children f/3 not linked" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/tests-launcher.py000077500000000000000000000063051436430751200203520ustar00rootroot00000000000000#!/usr/bin/env python3 """ author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2020, deadc0de6 tests launcher """ import os import sys import subprocess from concurrent import futures from halo import Halo LOG_FILE = '/tmp/dotdrop-tests-launcher.log' GITHUB_ENV = 'GITHUB_WORKFLOW' def is_cicd(): """are we in a CICD env (github actions)""" return GITHUB_ENV in os.environ def run_test(logfd, path): """run test pointed by path""" cur = os.path.dirname(sys.argv[0]) name = os.path.basename(path) path = os.path.join(cur, name) if logfd: logfd.write(f'starting test \"{path}\"\n') logfd.flush() proc = subprocess.Popen(path, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, encoding='utf-8', errors='ignore') out, _ = proc.communicate() ret = proc.returncode == 0 reason = 'returncode' if 'Traceback' in out: ret = False reason = 'traceback' if logfd: logfd.write(f'done test \"{path}\": ok:{ret}\n') logfd.flush() return ret, reason, path, out def get_tests(): """get all tests available in current directory""" tests = [] cur = os.path.dirname(sys.argv[0]) for (_, _, filenames) in os.walk(cur): for path in filenames: if not path.endswith('.sh'): continue tests.append(path) break return tests def main(): """entry point""" max_jobs = None # number of processor if len(sys.argv) > 1: max_jobs = int(sys.argv[1]) tests = get_tests() logfd = sys.stdout if not is_cicd(): logfd = open(LOG_FILE, 'w', encoding='utf-8') if max_jobs: logfd.write(f'run tests with {max_jobs} parallel job(s)\n') logfd.write(f'running {len(tests)} test(s)\n') logfd.flush() print() spinner = None if not is_cicd(): # no spinner on github actions spinner = Halo(text='Testing', spinner='bouncingBall') spinner.start() with futures.ThreadPoolExecutor(max_workers=max_jobs) as ex: wait_for = {} for test in tests: j = ex.submit(run_test, logfd, test) wait_for[j] = test logfd.flush() for test in futures.as_completed(wait_for.keys()): try: ret, reason, name, log = test.result() except Exception as exc: print() print(f'test \"{wait_for[test]}\" failed: {exc}') logfd.close() return False if not ret: ex.shutdown(wait=False) for job in wait_for: job.cancel() print() print(log) print(f'test \"{name}\" failed: {reason}') logfd.close() return False sys.stdout.write('\n') if spinner: spinner.stop() print() logfd.write(f'done - ran {len(tests)} test(s)\n') logfd.close() return True if __name__ == '__main__': if not main(): sys.exit(1) sys.exit(0) dotdrop-1.12.9/tests-ng/toml.sh000077500000000000000000000045411436430751200163460ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test toml config # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpf="${tmp}/dotfiles" mkdir -p ${tmpf} echo "dotfiles source (dotpath): ${tmpf}" # create the config file cfg="${tmp}/config.toml" echo "config file: ${cfg}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmp}" clear_on_exit "${tmpd}" ## RELATIVE cat > ${cfg} << _EOF [config] backup = true create = true dotpath = "dotfiles" [dotfiles.f_abc] dst = "${tmpd}/abc" src = "abc" link = true [profiles.p1] dotfiles = [ "f_abc",] _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmpf}/abc echo "{{@@ profile @@}}" > ${tmpd}/def # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V [ ! -e ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 # import cd ${ddpath} | ${bin} import -f -c ${cfg} -p p1 -b -V ${tmpd}/def [ ! -e ${tmpf}${tmpd}/def ] && echo "[ERROR] dotfile not imported" && exit 1 # checks cnt=$(cd ${ddpath} | ${bin} files -G -c ${cfg} -p p1 -V | grep '^f_' | wc -l) [ "${cnt}" != "2" ] && echo "[ERROR]" && exit 1 ## CLEANING rm -rf ${tmp} ${tmpd} echo "OK" exit 0 dotdrop-1.12.9/tests-ng/transformations-template.sh000077500000000000000000000070311436430751200224320ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test transformations using templates # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF trans_read: r_echo_abs_src: echo "\$(cat {0}); {{@@ _dotfile_abs_src @@}}" > {1} r_echo_var: echo "\$(cat {0}); {{@@ r_var @@}}" > {1} reverse_it: cat {0} | rev > {1} trans_write: w_echo_key: echo "\$(cat {0}); {{@@ _dotfile_key @@}}" > {1} w_echo_var: echo "\$(cat {0}); {{@@ w_var @@}}" > {1} variables: r_var: readvar w_var: writevar config: backup: true create: true dotpath: dotfiles dotfiles: f_def: dst: ${tmpd}/def src: def f_abc: dst: ${tmpd}/abc src: abc trans_read: r_echo_abs_src trans_write: w_echo_key f_ghi: dst: ${tmpd}/ghi src: ghi trans_read: r_echo_var trans_write: w_echo_var f_rev: dst: ${tmpd}/rev src: rev trans_read: reverse_it profiles: p1: dotfiles: - f_abc - f_def - f_ghi - f_rev _EOF #cat ${cfg} # create the dotfiles echo 'abc' > ${tmps}/dotfiles/abc echo 'marker' > ${tmps}/dotfiles/def echo 'ghi' > ${tmps}/dotfiles/ghi echo '{{@@ profile @@}}' | rev > ${tmps}/dotfiles/rev ########################### # test install and compare ########################### # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # check dotfile [ ! -e ${tmpd}/def ] && exit 1 [ ! -e ${tmpd}/abc ] && exit 1 [ ! -e ${tmpd}/ghi ] && exit 1 [ ! -e ${tmpd}/rev ] && exit 1 grep marker ${tmpd}/def cat ${tmpd}/abc grep "^abc; ${tmps}/dotfiles/abc$" ${tmpd}/abc cat ${tmpd}/ghi grep "^ghi; readvar$" ${tmpd}/ghi cat ${tmpd}/rev grep "^p1$" ${tmpd}/rev ########################### # test update ########################### # update single file cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V # checks [ ! -e ${tmps}/dotfiles/def ] && exit 1 [ ! -e ${tmps}/dotfiles/abc ] && exit 1 [ ! -e ${tmps}/dotfiles/ghi ] && exit 1 [ ! -e ${tmps}/dotfiles/rev ] && exit 1 grep marker ${tmps}/dotfiles/def cat ${tmps}/dotfiles/abc grep "^abc; ${tmps}/dotfiles/abc; f_abc$" ${tmps}/dotfiles/abc cat ${tmps}/dotfiles/ghi grep "^ghi; readvar; writevar$" ${tmps}/dotfiles/ghi echo "OK" exit 0 dotdrop-1.12.9/tests-ng/transformations-with-args.sh000077500000000000000000000065641436430751200225360ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test transformations with args and templates # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF trans_read: r_echo_abs_src: echo "\$(cat {0}); {{@@ _dotfile_abs_src @@}}; {2}" > {1} r_echo_var: echo "\$(cat {0}); {{@@ r_var @@}}; {2}" > {1} trans_write: w_echo_key: echo "\$(cat {0}); {{@@ _dotfile_key @@}}; {2}" > {1} w_echo_var: echo "\$(cat {0}); {{@@ w_var @@}}; {2}" > {1} variables: r_var: readvar w_var: writevar config: backup: true create: true dotpath: dotfiles dotfiles: f_def: dst: ${tmpd}/def src: def f_abc: dst: ${tmpd}/abc src: abc trans_read: r_echo_abs_src arg1 trans_write: w_echo_key arg2 f_ghi: dst: ${tmpd}/ghi src: ghi trans_read: r_echo_var "{{@@ profile @@}}" trans_write: w_echo_var "{{@@ _dotfile_key @@}}" profiles: p1: dotfiles: - f_abc - f_def - f_ghi _EOF #cat ${cfg} # create the dotfiles echo 'abc' > ${tmps}/dotfiles/abc echo 'marker' > ${tmps}/dotfiles/def echo 'ghi' > ${tmps}/dotfiles/ghi ########################### # test install and compare ########################### # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # check dotfile [ ! -e ${tmpd}/def ] && exit 1 [ ! -e ${tmpd}/abc ] && exit 1 [ ! -e ${tmpd}/ghi ] && exit 1 grep marker ${tmpd}/def cat ${tmpd}/abc grep "^abc; ${tmps}/dotfiles/abc; arg1$" ${tmpd}/abc cat ${tmpd}/ghi grep "^ghi; readvar; p1$" ${tmpd}/ghi ########################### # test update ########################### # update single file cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V # checks [ ! -e ${tmps}/dotfiles/def ] && exit 1 [ ! -e ${tmps}/dotfiles/abc ] && exit 1 [ ! -e ${tmps}/dotfiles/ghi ] && exit 1 grep marker ${tmps}/dotfiles/def cat ${tmps}/dotfiles/abc grep "^abc; ${tmps}/dotfiles/abc; arg1; f_abc; arg2$" ${tmps}/dotfiles/abc cat ${tmps}/dotfiles/ghi grep "^ghi; readvar; p1; writevar; f_ghi$" ${tmps}/dotfiles/ghi echo "OK" exit 0 dotdrop-1.12.9/tests-ng/transformations.sh000077500000000000000000000134241436430751200206240ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test transformations # for install and compare # returns 1 in case of error # # exit on first error set -e #set -v # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" # token token="test-base64" tokend="compressed archive" touched="touched" cat > ${cfg} << _EOF trans_read: base64: cat {0} | base64 -d > {1} uncompress: mkdir -p {1} && tar -xf {0} -C {1} trans_write: base64: cat {0} | base64 > {1} compress: tar -cf {1} -C {0} . config: backup: true create: true dotpath: dotfiles dotfiles: f_def: dst: ${tmpd}/def src: def f_abc: dst: ${tmpd}/abc src: abc trans_read: base64 trans_write: base64 d_ghi: dst: ${tmpd}/ghi src: ghi trans_read: uncompress trans_write: compress chmod: 700 profiles: p1: dotfiles: - f_abc - f_def - d_ghi _EOF #cat ${cfg} # create the base64 dotfile tmpf=`mktemp --suffix='-dotdrop-tests' || mktemp -d` echo ${token} > ${tmpf} cat ${tmpf} | base64 > ${tmps}/dotfiles/abc rm -f ${tmpf} # create the canary dotfile echo 'marker' > ${tmps}/dotfiles/def # create the compressed dotfile tmpx=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmpx}" mkdir -p ${tmpx}/{a,b,c} mkdir -p ${tmpx}/a/{dir1,dir2} # ambiguous redirect ?? #echo ${tokend} > ${tmpd}/{a,b,c}/somefile echo ${tokend} > ${tmpx}/a/somefile echo ${tokend} > ${tmpx}/b/somefile echo ${tokend} > ${tmpx}/c/somefile echo ${tokend} > ${tmpx}/a/dir1/otherfile # create a fake file to ensure dir is created echo ${tokend} > ${tmpx}/a/dir2/token tar -cf ${tmps}/dotfiles/ghi -C ${tmpx} . rm -rf ${tmpx} tar -tf ${tmps}/dotfiles/ghi ########################### # test install and compare ########################### echo "[+] run install" # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # check canary dotfile [ ! -e ${tmpd}/def ] && echo "def does not exist" && exit 1 # check base64 dotfile [ ! -e ${tmpd}/abc ] && echo "abc does not exist" && exit 1 content=`cat ${tmpd}/abc` [ "${content}" != "${token}" ] && echo "bad content for abc" && exit 1 # check directory dotfile [ ! -e ${tmpd}/ghi/a/dir1/otherfile ] && echo "otherfile does not exist" && exit 1 content=`cat ${tmpd}/ghi/a/somefile` [ "${content}" != "${tokend}" ] && echo "bad content for somefile" && exit 1 content=`cat ${tmpd}/ghi/a/dir1/otherfile` [ "${content}" != "${tokend}" ] && echo "bad content for otherfile" && exit 1 # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "0" ] && echo "compare failed (0)" && exit 1 set -e # change base64 deployed file echo ${touched} > ${tmpd}/abc set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "compare failed (1)" && exit 1 set -e # change uncompressed deployed dotfile echo ${touched} > ${tmpd}/ghi/a/somefile set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "compare failed (2)" && exit 1 set -e ########################### # test update ########################### # update single file echo 'update' > ${tmpd}/def set +e cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V f_def [ "$?" != "0" ] && echo "update failed (1)" && exit 1 set -e [ ! -e ${tmpd}/def ] && echo 'dotfile in FS removed' && exit 1 [ ! -e ${tmps}/dotfiles/def ] && echo 'dotfile in dotpath removed' && exit 1 # update single file set +e cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V f_abc [ "$?" != "0" ] && echo "update failed (2)" && exit 1 set -e # test updated file [ ! -e ${tmps}/dotfiles/abc ] && echo "abc does not exist" && exit 1 content=`cat ${tmps}/dotfiles/abc` bcontent=`echo ${touched} | base64` [ "${content}" != "${bcontent}" ] && echo "bad content for abc" && exit 1 # update directory echo ${touched} > ${tmpd}/ghi/b/newfile rm -r ${tmpd}/ghi/c cd ${ddpath} | ${bin} update -f -k -c ${cfg} -p p1 -b -V d_ghi [ "$?" != "0" ] && echo "update failed" && exit 1 # test updated directory set +e tar -tf ${tmps}/dotfiles/ghi | grep './b/newfile' || (echo "newfile not found in tar" && exit 1) tar -tf ${tmps}/dotfiles/ghi | grep './a/dir1/otherfile' || (echo "otherfile not found in tar" && exit 1) set -e tmpy=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmpy}" tar -xf ${tmps}/dotfiles/ghi -C ${tmpy} content=`cat ${tmpy}/a/somefile` [ "${content}" != "${touched}" ] && echo "bad content" && exit 1 # check canary dotfile [ ! -e ${tmps}/dotfiles/def ] && echo "def not found" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-ignore-missing.sh000077500000000000000000000072461436430751200216120ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test missing files ignored as expected # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests-source' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt}/folder touch ${dt}/folder/a # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests-dest' || mktemp -d` cp -r ${dt}/folder ${tmpd}/ touch ${tmpd}/folder/b mkdir ${tmpd}/folder/c clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: thedotfile: dst: ${tmpd}/folder src: folder profiles: p1: dotfiles: - thedotfile _EOF #cat ${cfg} #tree ${dt} # # Test with no ignore-missing setting # # file b / folder c SHOULD be copied echo "[+] test with no ignore-missing setting" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key thedotfile [ ! -e ${dt}/folder/b ] && echo "should have been updated" && exit 1 [ ! -e ${dt}/folder/c ] && echo "should have been updated" && exit 1 # Reset rm ${dt}/folder/b rmdir ${dt}/folder/c # # Test with command-line flag # # file b / folder c should NOT be copied echo "[+] test with command-line flag" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key thedotfile --ignore-missing [ -e ${dt}/folder/b ] && echo "should not have been updated" && exit 1 [ -e ${dt}/folder/c ] && echo "should not have been updated" && exit 1 # # Test with global option # cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles ignore_missing_in_dotdrop: true dotfiles: thedotfile: dst: ${tmpd}/folder src: folder profiles: p1: dotfiles: - thedotfile _EOF # file b / folder c should NOT be copied echo "[+] test global option" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key thedotfile [ -e ${dt}/folder/b ] && echo "should not have been updated" && exit 1 [ -e ${dt}/folder/c ] && echo "should not have been updated" && exit 1 # # Test with dotfile option # cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: thedotfile: dst: ${tmpd}/folder src: folder ignore_missing_in_dotdrop: true profiles: p1: dotfiles: - thedotfile _EOF # file b / folder c should NOT be copied echo "[+] test dotfile option" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key thedotfile [ -e ${dt}/folder/b ] && echo "should not have been updated" && exit 1 [ -e ${dt}/folder/c ] && echo "should not have been updated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-ignore-relative.sh000077500000000000000000000044641436430751200217530ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test ignore update relative pattern # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt} mkdir -p ${dt}/a/{b,c} echo 'a' > ${dt}/a/b/abfile echo 'a' > ${dt}/a/c/acfile # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" cp -r ${dt}/a ${tmpd}/ # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/a src: a upignore: - "cfile" - "newfile" - "newdir" profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} #tree ${dt} # edit/add files echo "[+] edit/add files" touch ${tmpd}/a/newfile echo 'b' > ${tmpd}/a/c/acfile mkdir -p ${tmpd}/a/newdir/b touch ${tmpd}/a/newdir/b/c #tree ${tmpd}/a # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc #tree ${dt} # check files haven't been updated grep 'b' ${dt}/a/c/acfile >/dev/null || (echo "b not found" && exit 1) [ -e ${dt}/a/newfile ] && echo "new file does not exist" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-ignore.sh000077500000000000000000000045551436430751200201430ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test ignore update # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt} mkdir -p ${dt}/a/{b,c} echo 'a' > ${dt}/a/b/abfile echo 'a' > ${dt}/a/c/acfile # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" cp -r ${dt}/a ${tmpd}/ # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/a src: a upignore: - "*/cfile" - "*/newfile" - "*/newdir" profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} #tree ${dt} # edit/add files echo "[+] edit/add files" touch ${tmpd}/a/newfile echo 'b' > ${tmpd}/a/c/acfile mkdir -p ${tmpd}/a/newdir/b touch ${tmpd}/a/newdir/b/c # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc # check files haven't been updated grep_or_fail 'b' "${dt}/a/c/acfile" [ -e ${dt}/a/newfile ] && echo "should not have been updated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-negative-ignore-relative.sh000077500000000000000000000077271436430751200235600ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test ignore update negative relative pattern # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" mkdir -p ${basedir}/dotfiles/a/{b,c} echo 'a' > ${basedir}/dotfiles/a/b/abfile1 echo 'a' > ${basedir}/dotfiles/a/b/abfile2 echo 'a' > ${basedir}/dotfiles/a/b/abfile3 echo 'a' > ${basedir}/dotfiles/a/c/acfile # the dotfile to be updated tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" cp -r ${basedir}/dotfiles/a ${tmpd}/ # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/a src: a upignore: - "newdir/b/*" - "!newdir/b/d" - "b/abfile?" - "!b/abfile3" profiles: p1: dotfiles: - f_abc _EOF # edit/add files echo "[+] edit/add files" mkdir -p ${tmpd}/a/newdir/b echo 'b' > ${tmpd}/a/b/abfile1 echo 'b' > ${tmpd}/a/b/abfile2 echo 'b' > ${tmpd}/a/b/abfile3 echo 'b' > ${tmpd}/a/b/abfile4 touch ${tmpd}/a/newdir/b/{c,d} # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc # check files haven't been updated set +e grep a ${basedir}/dotfiles/a/b/abfile1 >/dev/null 2>&1 || (echo "abfile1 should not have been updated" && exit 1) grep a ${basedir}/dotfiles/a/b/abfile2 >/dev/null 2>&1 || (echo "abfile2 should not have been updated" && exit 1) grep b ${basedir}/dotfiles/a/b/abfile3 >/dev/null 2>&1 || (echo "abfile3 should have been updated" && exit 1) set -e [ -e ${basedir}/dotfiles/a/b/abfile4 ] && echo "abfile4 should not have been updated" && exit 1 [ -e ${basedir}/dotfiles/a/newdir/b/c ] && echo "newdir/b/c should not have been updated" && exit 1 [ ! -e ${basedir}/dotfiles/a/newdir/b/d ] && echo "newdir/b/d should have been updated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-negative-ignore.sh000077500000000000000000000077611436430751200217450ustar00rootroot00000000000000#!/usr/bin/env bash # author: jtt9340 (https://github.com/jtt9340) # # test negative ignore update # returns 1 in case of error # # exit on first error set -e # all this crap to get current path if [ $(uname) = Darwin ]; then # Unfortunately, readlink works differently on macOS than it does on GNU/Linux # (the -f option behaves differently) and the realpath command does not exist. # Workarounds I find on the Internet suggest just using Homebrew to install coreutils # so you can get the GNU coreutils on your Mac. But, I don't want this script to # assume (a) users have Homebrew installed and (b) if they have Homebrew installed, that # they then installed the GNU coreutils. readlink() { TARGET_FILE=$1 cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` # Iterate down a (possible) chain of symlinks while [ -L "$TARGET_FILE" ]; do TARGET_FILE=`readlink $TARGET_FILE` cd `dirname $TARGET_FILE` TARGET_FILE=`basename $TARGET_FILE` done # Compute the canonicalized name by finding the physical path # for the directory we're in and appending the target file. PHYS_DIR=`pwd -P` RESULT=$PHYS_DIR/$TARGET_FILE echo $RESULT } rl="readlink" else rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ -n "${1}" ] && ddpath="${1}" [ ! -d ${ddpath} ] && exho "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" mkdir -p ${basedir}/dotfiles/a/{b,c} echo 'a' > ${basedir}/dotfiles/a/b/abfile1 echo 'a' > ${basedir}/dotfiles/a/b/abfile2 echo 'a' > ${basedir}/dotfiles/a/b/abfile3 echo 'a' > ${basedir}/dotfiles/a/c/acfile # the dotfile to be updated tmpd=`mktemp -d --suffix='-dotdrop-tests' 2>/dev/null || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" cp -r ${basedir}/dotfiles/a ${tmpd}/ # create the config file cfg="${basedir}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/a src: a upignore: - "*/newdir/b/*" - "!*/newdir/b/d" - "*/abfile?" - "!*/abfile3" profiles: p1: dotfiles: - f_abc _EOF # edit/add files echo "[+] edit/add files" mkdir -p ${tmpd}/a/newdir/b echo 'b' > ${tmpd}/a/b/abfile1 echo 'b' > ${tmpd}/a/b/abfile2 echo 'b' > ${tmpd}/a/b/abfile3 echo 'b' > ${tmpd}/a/b/abfile4 touch ${tmpd}/a/newdir/b/{c,d} # update echo "[+] update" cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=p1 --key f_abc # check files haven't been updated grep_or_fail a ${basedir}/dotfiles/a/b/abfile1 grep_or_fail a ${basedir}/dotfiles/a/b/abfile2 grep_or_fail b ${basedir}/dotfiles/a/b/abfile3 [ -e ${basedir}/dotfiles/a/b/abfile4 ] && echo "abfile4 should not have been updated" && exit 1 [ -e ${basedir}/dotfiles/a/newdir/b/c ] && echo "newdir/b/c should not have been updated" && exit 1 [ ! -e ${basedir}/dotfiles/a/newdir/b/d ] && echo "newdir/b/d should have been updated" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-profile-check.sh000077500000000000000000000075621436430751200213740ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test update dotfile from different profile # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # $1 pattern # $2 path grep_or_fail() { grep "${1}" "${2}" >/dev/null 2>&1 || (echo "pattern not found in ${2}" && exit 1) } # dotdrop directory tmps=`mktemp -d --suffix='-dotdrop-tests-source' || mktemp -d` dt="${tmps}/dotfiles" mkdir -p ${dt} xori="profile x" xori="profile y" echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # fs dotfiles tmpd=`mktemp -d --suffix='-dotdrop-tests-dest' || mktemp -d` touch ${tmpd}/file clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: false create: true dotpath: dotfiles dotfiles: f_file_x: dst: ${tmpd}/file src: file_x f_file_y: dst: ${tmpd}/file src: file_y profiles: x: dotfiles: - f_file_x y: dotfiles: - f_file_y _EOF cat ${cfg} # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with key echo "test update x from key" n="patched content for X" echo "${n}" > ${tmpd}/file cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=x --key f_file_x grep_or_fail "${n}" "${dt}/file_x" grep_or_fail "${yori}" "${dt}/file_y" # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with key echo "test update y from key" n="patched content for Y" echo "${n}" > ${tmpd}/file cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=y --key f_file_y grep_or_fail "${n}" "${dt}/file_y" grep_or_fail "${xori}" "${dt}/file_x" # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with path echo "test update x from path" n="patched content for X" echo "${n}" > ${tmpd}/file cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=x "${tmpd}/file" grep_or_fail "${n}" "${dt}/file_x" grep_or_fail "${yori}" "${dt}/file_y" # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with path echo "test update y from path" n="patched content for Y" echo "${n}" > ${tmpd}/file cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=y "${tmpd}/file" grep_or_fail "${n}" "${dt}/file_y" grep_or_fail "${xori}" "${dt}/file_x" ## make sure it fails when wrong dotfile # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with key echo "test wrong key for x" n="patched content for X" echo "${n}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=x --key f_file_y set -e grep_or_fail "${xori}" "${dt}/file_x" grep_or_fail "${yori}" "${dt}/file_y" # reset echo "${xori}" > ${dt}/file_x echo "${yori}" > ${dt}/file_y # test with key echo "test wrong key for y" n="patched content for Y" echo "${n}" > ${tmpd}/file set +e cd ${ddpath} | ${bin} update -f -c ${cfg} --verbose --profile=y --key f_file_x set -e grep_or_fail "${xori}" "${dt}/file_x" grep_or_fail "${yori}" "${dt}/file_y" echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-rights.sh000077500000000000000000000050701436430751200201510ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2019, deadc0de6 # # test updates and rights # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile directory to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` # the dotfile file to be imported tmpf=`mktemp` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # single file echo 'file' > ${tmpf} mkdir ${tmpd}/dir1 echo 'dir1file1' > ${tmpd}/dir1/file1 echo 'dir1file2' > ${tmpd}/dir1/file2 # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import dir1 echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd} cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpf} # change file chmod +x ${tmpf} # update echo "[+] updating" cd ${ddpath} | ${bin} update -c ${cfg} -f --verbose ${tmpf} # test change applied [ "`stat -c '%a' ${tmpf}`" != "`stat -c '%a' ${basedir}/dotfiles/${tmpf}`" ] && exit 1 # change file chmod +x ${tmpd}/dir1/file2 echo 'test' > ${tmpd}/dir1/newfile chmod +x ${tmpd}/dir1/newfile # update echo "[+] updating" cd ${ddpath} | ${bin} update -c ${cfg} -f --verbose ${tmpd} # test change applied [ "`stat -c '%a' ${tmpd}/dir1/newfile`" != "`stat -c '%a' ${basedir}/dotfiles/${tmpd}/dir1/newfile`" ] && exit 1 [ "`stat -c '%a' ${tmpd}/dir1/file2`" != "`stat -c '%a' ${basedir}/dotfiles/${tmpd}/dir1/file2`" ] && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-templates.sh000077500000000000000000000051641436430751200206530ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test update of templates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles echo "dotfiles source (dotpath): ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" # the workdir tmpw=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` export DOTDROP_WORKDIR="${tmpw}" echo "workdir: ${tmpw}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" clear_on_exit "${tmpw}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "head" > ${tmps}/dotfiles/abc echo '{%@@ if profile == "p1" @@%}' >> ${tmps}/dotfiles/abc echo "is p1" >> ${tmps}/dotfiles/abc echo '{%@@ else @@%}' >> ${tmps}/dotfiles/abc echo "is not p1" >> ${tmps}/dotfiles/abc echo '{%@@ endif @@%}' >> ${tmps}/dotfiles/abc echo "tail" >> ${tmps}/dotfiles/abc # create the installed dotfile echo "head" > ${tmpd}/abc echo "is p1" >> ${tmpd}/abc echo "tail" >> ${tmpd}/abc # update #cat ${tmps}/dotfiles/abc set +e patch=`cd ${ddpath} | ${bin} update -P -p p1 -k f_abc --cfg ${cfg} 2>&1 | grep 'try patching with' | sed 's/"//g'` set -e patch=`echo ${patch} | sed 's/^.*: //g'` echo "patching with: ${patch}" eval ${patch} #cat ${tmps}/dotfiles/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update-with-key.sh000077500000000000000000000053611436430751200204150ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test updates with key # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # originally imported directory echo 'unique' > ${tmpd}/uniquefile uniquefile_key="f_uniquefile" echo 'unique2' > ${tmpd}/uniquefile2 uniquefile2_key="f_uniquefile2" mkdir ${tmpd}/dir1 touch ${tmpd}/dir1/dir1f1 mkdir ${tmpd}/dir1/dir1dir1 dir1_key="d_dir1" # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import dir1 echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/dir1 cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/uniquefile cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/uniquefile2 # make some modification echo "[+] modify" echo 'changed' > ${tmpd}/uniquefile echo 'changed' > ${tmpd}/uniquefile2 echo 'new' > ${tmpd}/dir1/dir1dir1/new # update by key echo "[+] updating single key" cd ${ddpath} | ${bin} update -c ${cfg} -k -f --verbose ${uniquefile_key} # ensure changes applied correctly (only to uniquefile) diff ${tmpd}/uniquefile ${basedir}/dotfiles/${tmpd}/uniquefile # should be same set +e diff ${tmpd}/uniquefile2 ${basedir}/dotfiles/${tmpd}/uniquefile2 # should be different [ "${?}" != "1" ] && exit 1 set -e # update all keys echo "[+] updating all keys" cd ${ddpath} | ${bin} update -c ${cfg} -k -f --verbose # ensure all changes applied diff ${tmpd} ${basedir}/dotfiles/${tmpd} echo "OK" exit 0 dotdrop-1.12.9/tests-ng/update.sh000077500000000000000000000071361436430751200166600ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test updates # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # dotdrop directory basedir=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "[+] dotdrop dir: ${basedir}" echo "[+] dotpath dir: ${basedir}/dotfiles" # the dotfile to be imported tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${basedir}" clear_on_exit "${tmpd}" # single file echo 'unique' > ${tmpd}/uniquefile # hierarchy from https://pymotw.com/2/filecmp/ # create the hierarchy # for dir1 (originally imported directory)))) mkdir ${tmpd}/dir1 touch ${tmpd}/dir1/file_only_in_dir1 mkdir -p ${tmpd}/dir1/dir_only_in_dir1 mkdir -p ${tmpd}/dir1/common_dir echo 'this file is the same' > ${tmpd}/dir1/common_file echo 'in dir1' > ${tmpd}/dir1/not_the_same echo 'This is a file in dir1' > ${tmpd}/dir1/file_in_dir1 mkdir -p ${tmpd}/dir1/sub/sub2 mkdir -p ${tmpd}/dir1/notindir2/notindir2 echo 'first' > ${tmpd}/dir1/sub/sub2/different #tree ${tmpd}/dir1 # create the hierarchy # for dir2 (modified original for update) mkdir ${tmpd}/dir2 touch ${tmpd}/dir2/file_only_in_dir2 mkdir -p ${tmpd}/dir2/dir_only_in_dir2 mkdir -p ${tmpd}/dir2/common_dir echo 'this file is the same' > ${tmpd}/dir2/common_file echo 'in dir2' > ${tmpd}/dir2/not_the_same mkdir -p ${tmpd}/dir2/file_in_dir1 mkdir -p ${tmpd}/dir2/sub/sub2 echo 'modified' > ${tmpd}/dir2/sub/sub2/different mkdir -p ${tmpd}/dir2/new/new2 mkdir -p ${tmpd}/dir2/sub1/sub2/sub3/ touch ${tmpd}/dir2/sub1/sub2/sub3/file #tree ${tmpd}/dir2 # create the config file cfg="${basedir}/config.yaml" create_conf ${cfg} # sets token # import dir1 echo "[+] import" cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/dir1 cd ${ddpath} | ${bin} import -f -c ${cfg} ${tmpd}/uniquefile # let's see the dotpath #tree ${basedir}/dotfiles # change dir1 to dir2 in deployed echo "[+] change dir" rm -rf ${tmpd}/dir1 mv ${tmpd}/dir2 ${tmpd}/dir1 #tree ${tmpd}/dir1 # change unique file echo 'changed' > ${tmpd}/uniquefile # compare #echo "[+] comparing" #cd ${ddpath} | ${bin} compare -c ${cfg} # update echo "[+] updating" cd ${ddpath} | ${bin} update -c ${cfg} -f --verbose ${tmpd}/uniquefile ${tmpd}/dir1 # manually update rm ${basedir}/dotfiles/${tmpd}/dir1/file_in_dir1 mkdir -p ${basedir}/dotfiles/${tmpd}/dir1/file_in_dir1 # ensure changes applied correctly diff ${tmpd}/dir1 ${basedir}/dotfiles/${tmpd}/dir1 diff ${tmpd}/uniquefile ${basedir}/dotfiles/${tmpd}/uniquefile [ ! -e ${basedir}/dotfiles/${tmpd}/dir1/sub1/sub2/sub3/file ] && echo "sub does not exist" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-ng/uservariables.sh000077500000000000000000000061641436430751200202450ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test user variables from yaml file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles import_variables: - uservariables.yaml:optional variables: var4: "variables_var4" dynvariables: var3: "echo dynvariables_var3" uservariables: var1: "var1" var2: "var2" var3: "var3" var4: "var4" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "var1: {{@@ var1 @@}}" > ${tmps}/dotfiles/abc echo "var2: {{@@ var2 @@}}" >> ${tmps}/dotfiles/abc echo "var3: {{@@ var3 @@}}" >> ${tmps}/dotfiles/abc echo "var4: {{@@ var4 @@}}" >> ${tmps}/dotfiles/abc # install echo "step 1" cd ${ddpath} | echo -e 'var1contentxxx\nvar2contentyyy\nvar3\nvar4\n' | ${bin} install -f -c ${cfg} -p p1 -V cat ${tmpd}/abc grep '^var1: var1contentxxx$' ${tmpd}/abc >/dev/null grep '^var2: var2contentyyy$' ${tmpd}/abc >/dev/null grep '^var3: dynvariables_var3$' ${tmpd}/abc >/dev/null grep '^var4: variables_var4$' ${tmpd}/abc >/dev/null [ ! -e "${tmps}/uservariables.yaml" ] && exit 1 grep '^variables:' ${tmps}/uservariables.yaml >/dev/null grep '^ var1: var1contentxxx$' ${tmps}/uservariables.yaml >/dev/null grep '^ var2: var2contentyyy$' ${tmps}/uservariables.yaml >/dev/null cat > "${tmps}/uservariables.yaml" << _EOF variables: var1: editedvar1 var2: editedvar2 _EOF echo "step 2" cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -V grep '^var1: editedvar1$' ${tmpd}/abc >/dev/null grep '^var2: editedvar2$' ${tmpd}/abc >/dev/null grep '^var3: dynvariables_var3$' ${tmpd}/abc >/dev/null grep '^var4: variables_var4$' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/variables-enrich.sh000077500000000000000000000071341436430751200206120ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2022, deadc0de6 # # test variables enrichment # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" export dotdrop_test_dst="${tmpd}/def" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "os={{@@ os @@}}" > ${tmps}/dotfiles/abc echo "release={{@@ release @@}}" >> ${tmps}/dotfiles/abc echo "distro_id={{@@ distro_id @@}}" >> ${tmps}/dotfiles/abc echo "distro_like={{@@ distro_like @@}}" >> ${tmps}/dotfiles/abc echo "distro_version={{@@ distro_version @@}}" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --verbose pybin="python3" real_os=$(${pybin} -c 'import platform; print(platform.system().lower())') real_release=$(${pybin} -c 'import platform; print(platform.release().lower())') real_distro_id=$(${pybin} -c 'import distro; print(distro.id().lower())') real_distro_like=$(${pybin} -c 'import distro; print(distro.like().lower())') real_distro_version=$(${pybin} -c 'import distro; print(distro.version().lower())') # tests [ ! -e ${tmpd}/abc ] && echo "abc not installed" && exit 1 cat "${tmpd}/abc" ## only test this on CI/CD grep "^os=${real_os}" ${tmpd}/abc >/dev/null grep "^release=${real_release}" ${tmpd}/abc >/dev/null grep "^distro_id=${real_distro_id}" ${tmpd}/abc >/dev/null grep "^distro_like=${real_distro_like}" ${tmpd}/abc >/dev/null grep "^distro_version=${real_distro_version}" ${tmpd}/abc >/dev/null # already defined variables cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: os: "abc" release: "def" distro_id: "ghi" distro_like: "jkl" distro_version: "mno" dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc _EOF rm -f "${tmpd}/abc" # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --verbose # tests [ ! -e ${tmpd}/abc ] && echo "abc not installed" && exit 1 cat "${tmpd}/abc" grep '^os=abc$' ${tmpd}/abc >/dev/null grep '^release=def$' ${tmpd}/abc >/dev/null grep '^distro_id=ghi$' ${tmpd}/abc >/dev/null grep '^distro_like=jkl$' ${tmpd}/abc >/dev/null grep '^distro_version=mno$' ${tmpd}/abc >/dev/null echo "OK" exit 0 dotdrop-1.12.9/tests-ng/variables-include.sh000077500000000000000000000050441436430751200207630ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test variables from yaml file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: var1: "this is some test" var2: 12 var3: another test dotfiles: f_abc: dst: ${tmpd}/abc src: abc profiles: p1: dotfiles: - f_abc variables: var1: "this is some sub-test" p2: include: - p1 variables: var2: 42 _EOF #cat ${cfg} # create the dotfile echo "{{@@ var1 @@}}" > ${tmps}/dotfiles/abc echo "{{@@ var2 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ var3 @@}}" >> ${tmps}/dotfiles/abc echo "test" >> ${tmps}/dotfiles/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 cat ${tmpd}/abc grep '^this is some sub-test' ${tmpd}/abc >/dev/null grep '^12' ${tmpd}/abc >/dev/null grep '^another test' ${tmpd}/abc >/dev/null # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p2 cat ${tmpd}/abc grep '^this is some sub-test' ${tmpd}/abc >/dev/null grep '^42' ${tmpd}/abc >/dev/null grep '^another test' ${tmpd}/abc >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/variables.sh000077500000000000000000000050661436430751200173460ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test variables from yaml file # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ # the dotfile source tmps=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` mkdir -p ${tmps}/dotfiles #echo "dotfile source: ${tmps}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` #echo "dotfile destination: ${tmpd}" clear_on_exit "${tmps}" clear_on_exit "${tmpd}" # create the config file cfg="${tmps}/config.yaml" export dotdrop_test_dst="${tmpd}/def" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles variables: var1: "this is some test" var2: 12 var3: another test vardst: "{{@@ env['dotdrop_test_dst'] @@}}" dotfiles: f_abc: dst: ${tmpd}/abc src: abc f_def: dst: "{{@@ vardst @@}}" src: def profiles: p1: dotfiles: - f_abc - f_def _EOF #cat ${cfg} # create the dotfile echo "{{@@ var1 @@}}" > ${tmps}/dotfiles/abc echo "{{@@ var2 @@}}" >> ${tmps}/dotfiles/abc echo "{{@@ var3 @@}}" >> ${tmps}/dotfiles/abc echo "test" >> ${tmps}/dotfiles/abc echo "test_def" > ${tmps}/dotfiles/def # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 --verbose [ ! -e ${tmpd}/abc ] && echo "abc not installed" && exit 1 grep '^this is some test' ${tmpd}/abc >/dev/null grep '^12' ${tmpd}/abc >/dev/null grep '^another test' ${tmpd}/abc >/dev/null [ ! -e ${tmpd}/def ] && echo "def not installed" && exit 1 grep '^test_def' ${tmpd}/def >/dev/null #cat ${tmpd}/abc echo "OK" exit 0 dotdrop-1.12.9/tests-ng/workdir-compare.sh000077500000000000000000000070601436430751200204770ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2021, deadc0de6 # # test workdir compare and warn on untracked files # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ unset DOTDROP_WORKDIR string="blabla" # the dotfile source tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpf="${tmp}/dotfiles" tmpw="${tmp}/workdir" export DOTDROP_WORKDIR="${tmpw}" mkdir -p ${tmpf} echo "dotfiles source (dotpath): ${tmpf}" mkdir -p ${tmpw} echo "workdir: ${tmpw}" # create the config file cfg="${tmp}/config.yaml" echo "config file: ${cfg}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmp}" clear_on_exit "${tmpd}" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} compare_workdir: true dotfiles: f_a: dst: ${tmpd}/a src: a link: link f_b: dst: ${tmpd}/b src: b link: nolink d_c: dst: ${tmpd}/c src: c link: link_children profiles: p1: dotfiles: - f_a - f_b - d_c _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmpf}/a echo "{{@@ profile @@}}" > ${tmpf}/b mkdir -p ${tmpf}/c echo "{{@@ profile @@}}" > ${tmpf}/c/a echo "{{@@ profile @@}}" > ${tmpf}/c/b mkdir ${tmpf}/c/x echo "{{@@ profile @@}}" > ${tmpf}/c/x/a echo "{{@@ profile @@}}" > ${tmpf}/c/x/b # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b # compare (no diff) cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V # add file touch ${tmpw}/untrack # compare (one diff) set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "not found untracked file in workdir (1)" && exit 1 set -e # clean rm ${tmpw}/untrack # add sub file touch ${tmpw}/${tmpd}/c/x/untrack # compare (two diff) set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "not found untracked file in workdir (2)" && exit 1 set -e # clean rm ${tmpw}/${tmpd}/c/x/untrack # add dir mkdir ${tmpw}/d_untrack touch ${tmpw}/d_untrack/untrack # compare (three diffs) set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "not found untracked file in workdir (3)" && exit 1 set -e # clean rm -r ${tmpw}/d_untrack # add sub dir mkdir ${tmpw}/${tmpd}/c/x/d_untrack touch ${tmpw}/${tmpd}/c/x/d_untrack/untrack # compare set +e cd ${ddpath} | ${bin} compare -c ${cfg} -p p1 -b -V [ "$?" != "1" ] && echo "not found untracked file in workdir (4)" && exit 1 set -e ## CLEANING rm -rf ${tmp} ${tmpd} echo "OK" exit 0 dotdrop-1.12.9/tests-ng/workdir.sh000077500000000000000000000111431436430751200170500ustar00rootroot00000000000000#!/usr/bin/env bash # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # # test workdir relative or absolute # returns 1 in case of error # # exit on first error set -e # all this crap to get current path rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found !" && exit 1 fi fi cur=$(dirname "$(${rl} "${0}")") #hash dotdrop >/dev/null 2>&1 #[ "$?" != "0" ] && echo "install dotdrop to run tests" && exit 1 #echo "called with ${1}" # dotdrop path can be pass as argument ddpath="${cur}/../" [ "${1}" != "" ] && ddpath="${1}" [ ! -d ${ddpath} ] && echo "ddpath \"${ddpath}\" is not a directory" && exit 1 export PYTHONPATH="${ddpath}:${PYTHONPATH}" bin="python3 -m dotdrop.dotdrop" hash coverage 2>/dev/null && bin="coverage run -a --source=dotdrop -m dotdrop.dotdrop" || true echo "dotdrop path: ${ddpath}" echo "pythonpath: ${PYTHONPATH}" # get the helpers source ${cur}/helpers echo -e "$(tput setaf 6)==> RUNNING $(basename $BASH_SOURCE) <==$(tput sgr0)" ################################################################ # this is the test ################################################################ unset DOTDROP_WORKDIR string="blabla" # the dotfile source tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpf="${tmp}/dotfiles" tmpw="${tmp}/workdir" export DOTDROP_WORKDIR="${tmpw}" mkdir -p ${tmpf} echo "dotfiles source (dotpath): ${tmpf}" mkdir -p ${tmpw} echo "workdir: ${tmpw}" # create the config file cfg="${tmp}/config.yaml" echo "config file: ${cfg}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmp}" clear_on_exit "${tmpd}" ## RELATIVE echo "RUNNING RELATIVE" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: `echo ${tmpw} | sed 's/^.*\///g'` dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmpf}/abc echo "${string}" >> ${tmpf}/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks grep -r p1 ${tmpw} >/dev/null grep -r ${string} ${tmpw} >/dev/null [ ! -e ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 ## CLEANING rm -rf ${tmp} ${tmpd} ## ABSOLUTE echo "RUNNING ABSOLUTE" # the dotfile source tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpf="${tmp}/dotfiles" tmpw="${tmp}/workdir" export DOTDROP_WORKDIR="${tmpw}" mkdir -p ${tmpf} echo "dotfiles source (dotpath): ${tmpf}" mkdir -p ${tmpw} echo "workdir: ${tmpw}" # create the config file cfg="${tmp}/config.yaml" echo "config file: ${cfg}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmp}" clear_on_exit "${tmpd}" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles workdir: ${tmpw} dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmpf}/abc echo "${string}" >> ${tmpf}/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks grep -r p1 ${tmpw} >/dev/null grep -r ${string} ${tmpw} >/dev/null [ ! -e ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 ## CLEANING rm -rf ${tmp} ${tmpd} ## NONE echo "RUNNING UNDEFINED WORKDIR" # the dotfile source tmp=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` tmpf="${tmp}/dotfiles" mkdir -p ${tmpf} echo "dotfiles source (dotpath): ${tmpf}" # create the config file cfg="${tmp}/config.yaml" echo "config file: ${cfg}" # the dotfile destination tmpd=`mktemp -d --suffix='-dotdrop-tests' || mktemp -d` echo "dotfiles destination: ${tmpd}" clear_on_exit "${tmp}" clear_on_exit "${tmpd}" cat > ${cfg} << _EOF config: backup: true create: true dotpath: dotfiles dotfiles: f_abc: dst: ${tmpd}/abc src: abc link: true profiles: p1: dotfiles: - f_abc _EOF #cat ${cfg} # create the dotfile echo "{{@@ profile @@}}" > ${tmpf}/abc echo "${string}" >> ${tmpf}/abc # install cd ${ddpath} | ${bin} install -f -c ${cfg} -p p1 -b -V # checks #grep -r p1 ${tmpw} >/dev/null #grep -r ${string} ${tmpw} >/dev/null [ ! -e ${tmpd}/abc ] && echo "[ERROR] dotfile not installed" && exit 1 [ ! -h ${tmpd}/abc ] && echo "[ERROR] dotfile is not a symlink" && exit 1 echo "OK" exit 0 dotdrop-1.12.9/tests-requirements.txt000066400000000000000000000004001436430751200177020ustar00rootroot00000000000000pycodestyle; python_version > '3.5' nose2; python_version > '3.5' coverage; python_version > '3.5' coveralls; python_version > '3.5' pyflakes; python_version > '3.5' pylint; python_version > '3.5' halo; python_version > '3.5' distro; python_version > '3.5'dotdrop-1.12.9/tests.sh000077500000000000000000000017771436430751200150010ustar00rootroot00000000000000#!/bin/sh # author: deadc0de6 (https://github.com/deadc0de6) # Copyright (c) 2017, deadc0de6 # stop on first error #set -ev set -e rl="readlink -f" if ! ${rl} "${0}" >/dev/null 2>&1; then rl="realpath" if ! hash ${rl}; then echo "\"${rl}\" not found!" && exit 1 fi fi cur=`dirname $(${rl} "${0}")` # make sure both version.py and manpage dotdrop.1 are in sync dotdrop_version=$(grep version dotdrop/version.py | sed 's/^.*= .\(.*\).$/\1/g') man_version=$(grep '^\.TH' manpage/dotdrop.1 | sed 's/^.*"dotdrop-\(.*\)\" "Save your.*$/\1/g') if [ "${dotdrop_version}" != "${man_version}" ]; then echo "ERROR version.py (${dotdrop_version}) and manpage (${man_version}) differ!" exit 1 fi echo "current version ${dotdrop_version}" # test syntax echo "checking syntax..." ${cur}/test-syntax.sh # test doc echo "checking documentation..." ${cur}/test-doc.sh # unittest echo "unittest..." ${cur}/test-unittest.sh # tests-ng echo "tests-ng..." ${cur}/test-ng.sh ## done echo "All tests finished successfully" dotdrop-1.12.9/tests/000077500000000000000000000000001436430751200144265ustar00rootroot00000000000000dotdrop-1.12.9/tests/__init__.py000066400000000000000000000000001436430751200165250ustar00rootroot00000000000000dotdrop-1.12.9/tests/dummy.py000066400000000000000000000004431436430751200161340ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the import function """ import unittest import dotdrop class TestDummy(unittest.TestCase): dotdrop.main() def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/helpers.py000066400000000000000000000200501436430751200164370ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 helpers for the unittests """ import os import random import shutil import string import tempfile from unittest import TestCase from ruamel.yaml import YAML as yaml from dotdrop.options import Options from dotdrop.linktypes import LinkTypes from dotdrop.utils import strip_home TMPSUFFIX = '-dotdrop-tests' class SubsetTestCase(TestCase): def assertIsSubset(self, sub, sup): for subKey, subValue in sub.items(): self.assertIn(subKey, sup) supValue = sup[subKey] if isinstance(subValue, str): self.assertEqual(subValue, supValue) continue if isinstance(subValue, dict): self.assertIsSubset(subValue, supValue) continue try: iter(subValue) self.assertTrue(all( subItem in supValue for subItem in subValue )) except TypeError: self.assertEqual(subValue, supValue) def clean(path): """Delete file or directory""" if not os.path.exists(path): return if os.path.islink(path): os.remove(path) elif os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) def get_string(length): """Get a random string of length 'length'""" alpha = string.ascii_uppercase + string.digits temp = ''.join(random.choice(alpha) for _ in range(length)) return 'tmp.{}{}'.format(temp, TMPSUFFIX) def get_tempdir(): """Get a temporary directory""" tmpdir = tempfile.mkdtemp(suffix=TMPSUFFIX) os.chmod(tmpdir, 0o755) return tmpdir def create_random_file(directory, content=None, binary=False, template=False): """Create a new file in directory with random content""" fname = get_string(8) mode = 'w' if binary: mode = 'wb' if content is None: if binary: pre = bytes() if template: pre = bytes('{{@@ header() @@}}\n', 'ascii') content = bytes('{}{}\n'.format(pre, get_string(100)), 'ascii') else: pre = '' if template: pre = '{{@@ header() @@}}\n' content = '{}{}\n'.format(pre, get_string(100)) path = os.path.join(directory, fname) with open(path, mode) as f: f.write(content) return path, content def edit_content(path, newcontent, binary=False): """edit file content""" mode = 'w' if binary: mode = 'wb' with open(path, mode) as f: f.write(newcontent) def create_dir(path): """Create a directory""" if not os.path.exists(path): os.mkdir(path) return path def _fake_args(): args = {} args['--verbose'] = False args['--no-banner'] = False args['--dry'] = False args['--force'] = False args['--nodiff'] = False args['--showdiff'] = True args['--link'] = 'nolink' args['--template'] = False args['--temp'] = False args[''] = [] args['--dopts'] = '' args['--file'] = [] args['--ignore'] = [] args[''] = [] args['--key'] = False args['--ignore'] = [] args['--show-patch'] = False args['--force-actions'] = False args['--grepable'] = False args['--as'] = None args['--file-only'] = False args['--workers'] = 1 args['--preserve-mode'] = False args['--ignore-missing'] = False args['--workdir-clear'] = False args['--transw'] = '' args['--transr'] = '' # cmds args['profiles'] = False args['files'] = False args['install'] = False args['compare'] = False args['import'] = False args['update'] = False args['detail'] = False args['remove'] = False return args def load_options(confpath, profile): """Load the config file from path""" # create the fake args (bypass docopt) args = _fake_args() args['--cfg'] = confpath args['--profile'] = profile args['--verbose'] = True # and get the options o = Options(args=args) o.profile = profile o.dry = False o.safe = False o.install_diff = True o.import_link = LinkTypes.NOLINK o.install_showdiff = True o.debug = True return o def get_path_strip_version(path): """Return the path of a file as stored in yaml config""" path = strip_home(path) path = path.lstrip('.' + os.sep) return path def get_dotfile_from_yaml(dic, path): """Return the dotfile from the yaml dictionary""" # path is not the file in dotpath but on the FS dotfiles = dic['dotfiles'] # src = get_path_strip_version(path) home = os.path.expanduser('~') if path.startswith(home): path = path.replace(home, '~') dotfile = [d for d in dotfiles.values() if d['dst'] == path] if dotfile: return dotfile[0] return None def yaml_dashed_list(items, indent=0): return ('\n'.join('{}- {}'.format(' ' * indent, item) for item in items) + '\n') def create_fake_config(directory, configname='config.yaml', dotpath='dotfiles', backup=True, create=True, import_configs=(), import_actions=(), import_variables=()): """Create a fake config file""" path = os.path.join(directory, configname) workdir = os.path.join(directory, 'workdir') with open(path, 'w') as f: f.write('config:\n') f.write(' backup: {}\n'.format(str(backup))) f.write(' create: {}\n'.format(str(create))) f.write(' dotpath: {}\n'.format(dotpath)) f.write(' workdir: {}\n'.format(workdir)) if import_actions: f.write(' import_actions:\n') f.write(yaml_dashed_list(import_actions, 4)) if import_configs: f.write(' import_configs:\n') f.write(yaml_dashed_list(import_configs, 4)) if import_variables: f.write(' import_variables:\n') f.write(yaml_dashed_list(import_variables, 4)) f.write('dotfiles:\n') f.write('profiles:\n') f.write('actions:\n') return path def create_yaml_keyval(pairs, parent_dir=None, top_key=None): if top_key: pairs = {top_key: pairs} if not parent_dir: parent_dir = get_tempdir() _, file_name = tempfile.mkstemp(dir=parent_dir, suffix='.yaml', text=True) yaml_dump(pairs, file_name) return file_name def populate_fake_config(config, dotfiles={}, profiles={}, actions={}, trans={}, trans_write={}, variables={}, dynvariables={}): """Adds some juicy content to config files""" is_path = isinstance(config, str) if is_path: config_path = config config = yaml_load(config_path) config['dotfiles'] = dotfiles config['profiles'] = profiles config['actions'] = actions config['trans_read'] = trans config['trans_write'] = trans_write config['variables'] = variables config['dynvariables'] = dynvariables if is_path: yaml_dump(config, config_path) def file_in_yaml(yaml_file, path, link=False): """Return whether path is in the given yaml file as a dotfile.""" strip = get_path_strip_version(path) if isinstance(yaml_file, str): yaml_conf = yaml_load(yaml_file) else: yaml_conf = yaml_file dotfiles = yaml_conf['dotfiles'].values() in_src = any([x['src'].endswith(strip) for x in dotfiles]) in_dst = path in (os.path.expanduser(x['dst']) for x in dotfiles) if link: df = get_dotfile_from_yaml(yaml_conf, path) has_link = False if df: has_link = 'link' in df else: return False return in_src and in_dst and has_link return in_src and in_dst def yaml_load(path): with open(path, 'r') as f: content = yaml(typ='safe').load(f) return content def yaml_dump(content, path): with open(path, 'w') as f: y = yaml() y.default_flow_style = False y.indent = 2 y.typ = 'safe' y.dump(content, f) dotdrop-1.12.9/tests/test_compare.py000066400000000000000000000140541436430751200174710ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the compare function """ import unittest import os from dotdrop.dotdrop import cmd_importer from dotdrop.dotdrop import cmd_compare from dotdrop.installer import Installer from dotdrop.comparator import Comparator from dotdrop.templategen import Templategen # from tests.helpers import * from tests.helpers import create_dir, get_string, get_tempdir, clean, \ create_random_file, create_fake_config, load_options, edit_content class TestCompare(unittest.TestCase): CONFIG_BACKUP = False CONFIG_CREATE = True CONFIG_DOTPATH = 'dotfiles' CONFIG_NAME = 'config.yaml' def compare(self, o, tmp, nbdotfiles): dotfiles = o.dotfiles self.assertTrue(len(dotfiles) == nbdotfiles) t = Templategen(base=o.dotpath, debug=True) inst = Installer(create=o.create, backup=o.backup, dry=o.dry, base=o.dotpath, debug=o.debug) comp = Comparator() results = {} for dotfile in dotfiles: path = os.path.expanduser(dotfile.dst) ret, err, insttmp = inst.install_to_temp(t, tmp, dotfile.src, dotfile.dst) if not ret: results[path] = False continue diff = comp.compare(insttmp, dotfile.dst, ignore=['whatever', 'whatelse']) results[path] = diff == '' return results def test_none(self): t = Templategen(base=self.CONFIG_DOTPATH, debug=True, variables=None) self.assertTrue(t is not None) def test_compare(self): """Test the compare function""" # setup some directories fold_config = os.path.join(os.path.expanduser('~'), '.config') create_dir(fold_config) fold_subcfg = os.path.join(os.path.expanduser('~'), '.config', get_string(5)) create_dir(fold_subcfg) self.addCleanup(clean, fold_subcfg) fold_tmp = get_tempdir() create_dir(fold_tmp) self.addCleanup(clean, fold_tmp) # create the directories tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) dotfilespath = get_tempdir() self.assertTrue(os.path.exists(dotfilespath)) self.addCleanup(clean, dotfilespath) # create the dotfiles to test d1, c1 = create_random_file(fold_config) self.assertTrue(os.path.exists(d1)) self.addCleanup(clean, d1) d2, c2 = create_random_file(fold_subcfg) self.assertTrue(os.path.exists(d2)) self.addCleanup(clean, d2) d3, c3 = create_random_file(fold_tmp) self.assertTrue(os.path.exists(d3)) self.addCleanup(clean, d3) d4, c4 = create_random_file(fold_tmp, binary=True) self.assertTrue(os.path.exists(d4)) self.addCleanup(clean, d4) d5 = get_tempdir() self.assertTrue(os.path.exists(d5)) self.addCleanup(clean, d5) d6, _ = create_random_file(d5) self.assertTrue(os.path.exists(d6)) d9 = get_tempdir() self.assertTrue(os.path.exists(d9)) self.addCleanup(clean, d9) d9sub = os.path.join(d9, get_string(5)) create_dir(d9sub) d9f1, _ = create_random_file(d9sub) # create the config file profile = get_string(5) confpath = create_fake_config(dotfilespath, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) self.assertTrue(os.path.exists(confpath)) o = load_options(confpath, profile) o.longkey = True o.debug = True dfiles = [d1, d2, d3, d4, d5, d9] # import the files o.import_path = dfiles cmd_importer(o) o = load_options(confpath, profile) # compare the files expected = {d1: True, d2: True, d3: True, d4: True, d5: True, d9: True} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # modify file edit_content(d1, get_string(20)) expected = {d1: False, d2: True, d3: True, d4: True, d5: True, d9: True} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # modify binary file edit_content(d4, bytes(get_string(20), 'ascii'), binary=True) expected = {d1: False, d2: True, d3: True, d4: False, d5: True, d9: True} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # add file in directory d7, _ = create_random_file(d5) self.assertTrue(os.path.exists(d7)) expected = {d1: False, d2: True, d3: True, d4: False, d5: False, d9: True} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # modify all files edit_content(d2, get_string(20)) edit_content(d3, get_string(21)) expected = {d1: False, d2: False, d3: False, d4: False, d5: False, d9: True} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # edit sub file edit_content(d9f1, get_string(12)) expected = {d1: False, d2: False, d3: False, d4: False, d5: False, d9: False} results = self.compare(o, tmp, len(dfiles)) self.assertTrue(results == expected) # test compare from dotdrop self.assertFalse(cmd_compare(o, tmp)) # test focus o.compare_focus = [d4] self.assertFalse(cmd_compare(o, tmp)) o.compare_focus = ['/tmp/fake'] self.assertFalse(cmd_compare(o, tmp)) def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_import.py000066400000000000000000000356221436430751200173610ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the import function """ import unittest import os from dotdrop.dotdrop import cmd_importer from dotdrop.dotdrop import cmd_list_profiles from dotdrop.dotdrop import cmd_files from dotdrop.dotdrop import cmd_update from dotdrop.linktypes import LinkTypes from tests.helpers import (clean, create_dir, create_fake_config, create_random_file, edit_content, file_in_yaml, get_path_strip_version, get_string, get_tempdir, load_options, populate_fake_config, yaml_load) class TestImport(unittest.TestCase): CONFIG_BACKUP = False CONFIG_CREATE = True CONFIG_DOTPATH = 'dotfiles' CONFIG_NAME = 'config.yaml' def load_yaml(self, path): """Load yaml to dict""" self.assertTrue(os.path.exists(path)) return yaml_load(path) def assert_file(self, path, o, profile): """Make sure path has been inserted in conf for profile""" strip = get_path_strip_version(path) self.assertTrue(any(x.src.endswith(strip) for x in o.dotfiles)) dsts = (os.path.expanduser(x.dst) for x in o.dotfiles) self.assertTrue(path in dsts) def assert_in_yaml(self, path, dic, link=False): """Make sure "path" is in the "dic" representing the yaml file""" self.assertTrue(file_in_yaml(dic, path, link=link)) def test_import(self): """Test the import function""" # on filesystem src = get_tempdir() self.assertTrue(os.path.exists(src)) self.addCleanup(clean, src) # in dotdrop dotfilespath = get_tempdir() self.assertTrue(os.path.exists(dotfilespath)) self.addCleanup(clean, dotfilespath) profile = get_string(10) confpath = create_fake_config(dotfilespath, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) self.assertTrue(os.path.exists(confpath)) o = load_options(confpath, profile) # create some random dotfiles dotfile1, content1 = create_random_file(src) self.addCleanup(clean, dotfile1) dotfile2, content2 = create_random_file(os.path.expanduser('~')) self.addCleanup(clean, dotfile2) homeconf = os.path.join(os.path.expanduser('~'), '.config') if not os.path.exists(homeconf): os.mkdir(homeconf) self.addCleanup(clean, homeconf) dotconfig = os.path.join(homeconf, get_string(5)) create_dir(dotconfig) self.addCleanup(clean, dotconfig) dotfile3, content3 = create_random_file(dotconfig) dotfile4, content3 = create_random_file(homeconf) self.addCleanup(clean, dotfile4) # fake a directory containing dotfiles dotfile5 = get_tempdir() self.assertTrue(os.path.exists(dotfile5)) self.addCleanup(clean, dotfile5) sub1, _ = create_random_file(dotfile5) sub2, _ = create_random_file(dotfile5) # fake a file for symlink dotfile6, content6 = create_random_file(dotconfig) self.addCleanup(clean, dotfile6) # fake a directory for symlink dotfile7 = get_tempdir() self.assertTrue(os.path.exists(dotfile7)) self.addCleanup(clean, dotfile7) sub3, _ = create_random_file(dotfile7) sub4, _ = create_random_file(dotfile7) # import the dotfiles dfiles = [dotfile1, dotfile2, dotfile3, dotfile4, dotfile5] o.import_path = dfiles cmd_importer(o) # import symlink o.import_link = LinkTypes.LINK sfiles = [dotfile6, dotfile7] o.import_path = sfiles cmd_importer(o) o.import_link = LinkTypes.NOLINK # reload the config o = load_options(confpath, profile) # test dotfiles in config class self.assertTrue(profile in [p.key for p in o.profiles]) self.assert_file(dotfile1, o, profile) self.assert_file(dotfile2, o, profile) self.assert_file(dotfile3, o, profile) self.assert_file(dotfile4, o, profile) self.assert_file(dotfile5, o, profile) self.assert_file(dotfile6, o, profile) self.assert_file(dotfile7, o, profile) # test dotfiles in yaml file y = self.load_yaml(confpath) self.assert_in_yaml(dotfile1, y) self.assert_in_yaml(dotfile2, y) self.assert_in_yaml(dotfile3, y) self.assert_in_yaml(dotfile4, y) self.assert_in_yaml(dotfile5, y) self.assert_in_yaml(dotfile6, y, link=True) self.assert_in_yaml(dotfile7, y, link=True) # test have been imported in dotdrop dotpath directory indt1 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile1)) self.assertTrue(os.path.exists(indt1)) indt2 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile2)) self.assertTrue(os.path.exists(indt2)) indt3 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile3)) self.assertTrue(os.path.exists(indt3)) indt4 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile4)) self.assertTrue(os.path.exists(indt4)) indt5 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile5)) self.assertTrue(os.path.exists(indt5)) s1 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile6), sub1) self.assertTrue(os.path.exists(s1)) s2 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile6), sub2) self.assertTrue(os.path.exists(s2)) indt6 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile6)) self.assertTrue(os.path.exists(indt6)) indt7 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile7)) self.assertTrue(os.path.exists(indt7)) s3 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile7), sub3) self.assertTrue(os.path.exists(s3)) s4 = os.path.join(dotfilespath, self.CONFIG_DOTPATH, get_path_strip_version(dotfile7), sub4) self.assertTrue(os.path.exists(s4)) cmd_list_profiles(o) cmd_files(o) # fake test update editcontent = 'edited' edit_content(dotfile1, editcontent) o.safe = False o.update_path = [dotfile1] o.debug = True cmd_update(o) c2 = open(indt1, 'r').read() self.assertTrue(editcontent == c2) def test_ext_config_yaml_not_mix(self): """Test whether the import_configs mixes yaml files upon importing.""" # dotfiles on filesystem src = get_tempdir() self.assertTrue(os.path.exists(src)) self.addCleanup(clean, src) # create some random dotfiles dotfiles = [] for _ in range(3): dotfile, _ = create_random_file(src) dotfiles.append(dotfile) self.addCleanup(clean, dotfile) self.assertTrue(all(map(os.path.exists, dotfiles))) # create dotdrop home dotdrop_home = get_tempdir() self.assertTrue(os.path.exists(dotdrop_home)) self.addCleanup(clean, dotdrop_home) dotpath_ed = 'imported' imported = { 'config': { 'dotpath': dotpath_ed, }, 'dotfiles': {}, 'profiles': { 'host1': { 'dotfiles': [], }, }, 'actions': { 'pre': { 'a_pre_log_ed': 'echo pre 2', }, 'post': { 'a_post_log_ed': 'echo post 2', }, 'a_log_ed': 'echo 2', }, 'trans': { 't_log_ed': 'echo 3', }, 'trans_write': { 'tw_log_ed': 'echo 4', }, 'variables': { 'v_log_ed': '42', }, 'dynvariables': { 'dv_log_ed': 'echo 5', }, } dotpath_ing = 'importing' importing = { 'config': { 'dotpath': dotpath_ing, }, 'dotfiles': {}, 'profiles': { 'host2': { 'dotfiles': [], 'include': ['host1'], }, }, 'actions': { 'pre': { 'a_pre_log_ing': 'echo pre a', }, 'post': { 'a_post_log_ing': 'echo post a', }, 'a_log_ing': 'echo a', }, 'trans': { 't_log_ing': 'echo b', }, 'trans_write': { 'tw_log_ing': 'echo c', }, 'variables': { 'v_log_ing': 'd', }, 'dynvariables': { 'dv_log_ing': 'echo e', }, } dotfiles_ing, dotfiles_ed = dotfiles[:-1], dotfiles[-1:] # create the imported base config file imported_path = create_fake_config(dotdrop_home, configname='config-2.yaml', **imported['config']) # create the importing base config file importing_path = create_fake_config(dotdrop_home, configname='config.yaml', import_configs=['config-2.yaml'], **importing['config']) # edit the imported config populate_fake_config(imported_path, **{ k: v for k, v in imported.items() if k != 'config' }) # edit the importing config populate_fake_config(importing_path, **{ k: v for k, v in importing.items() if k != 'config' }) # import the dotfiles o = load_options(imported_path, 'host1') o.import_path = dotfiles_ed cmd_importer(o) o = load_options(importing_path, 'host2') o.import_path = dotfiles_ing cmd_importer(o) # reload the config o = load_options(importing_path, 'host2') # test imported config y = self.load_yaml(imported_path) # testing dotfiles self.assertTrue(all(file_in_yaml(y, df) for df in dotfiles_ed)) self.assertFalse(any(file_in_yaml(y, df) for df in dotfiles_ing)) # testing profiles profiles = y['profiles'].keys() self.assertTrue('host1' in profiles) self.assertFalse('host2' in profiles) # testing actions actions = y['actions']['pre'] actions.update(y['actions']['post']) actions.update({ k: v for k, v in y['actions'].items() if k not in ('pre', 'post') }) actions = actions.keys() self.assertTrue(all(a.endswith('ed') for a in actions)) self.assertFalse(any(a.endswith('ing') for a in actions)) # testing transformations transformations = y['trans_read'].keys() self.assertTrue(all(t.endswith('ed') for t in transformations)) self.assertFalse(any(t.endswith('ing') for t in transformations)) transformations = y['trans_write'].keys() self.assertTrue(all(t.endswith('ed') for t in transformations)) self.assertFalse(any(t.endswith('ing') for t in transformations)) # testing variables variables = self._remove_priv_vars(y['variables'].keys()) self.assertTrue(all(v.endswith('ed') for v in variables)) self.assertFalse(any(v.endswith('ing') for v in variables)) dyn_variables = y['dynvariables'].keys() self.assertTrue(all(dv.endswith('ed') for dv in dyn_variables)) self.assertFalse(any(dv.endswith('ing') for dv in dyn_variables)) # test importing config y = self.load_yaml(importing_path) # testing dotfiles self.assertTrue(all(file_in_yaml(y, df) for df in dotfiles_ing)) self.assertFalse(any(file_in_yaml(y, df) for df in dotfiles_ed)) # testing profiles profiles = y['profiles'].keys() self.assertTrue('host2' in profiles) self.assertFalse('host1' in profiles) # testing actions actions = y['actions']['pre'] actions.update(y['actions']['post']) actions.update({ k: v for k, v in y['actions'].items() if k not in ('pre', 'post') }) actions = actions.keys() self.assertTrue(all(action.endswith('ing') for action in actions)) self.assertFalse(any(action.endswith('ed') for action in actions)) # testing transformations transformations = y['trans_read'].keys() self.assertTrue(all(t.endswith('ing') for t in transformations)) self.assertFalse(any(t.endswith('ed') for t in transformations)) transformations = y['trans_write'].keys() self.assertTrue(all(t.endswith('ing') for t in transformations)) self.assertFalse(any(t.endswith('ed') for t in transformations)) # testing variables variables = self._remove_priv_vars(y['variables'].keys()) self.assertTrue(all(v.endswith('ing') for v in variables)) self.assertFalse(any(v.endswith('ed') for v in variables)) dyn_variables = y['dynvariables'].keys() self.assertTrue(all(dv.endswith('ing') for dv in dyn_variables)) self.assertFalse(any(dv.endswith('ed') for dv in dyn_variables)) def _remove_priv_vars(self, variables_keys): variables = [v for v in variables_keys if not v.startswith('_')] if 'profile' in variables: variables.remove('profile') return variables def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_install.py000066400000000000000000000422411436430751200175100ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the install function """ import os import unittest from unittest.mock import MagicMock import filecmp from dotdrop.cfg_aggregator import CfgAggregator as Cfg from tests.helpers import (clean, create_dir, create_fake_config, create_random_file, get_string, get_tempdir, load_options, populate_fake_config) from dotdrop.dotfile import Dotfile from dotdrop.installer import Installer from dotdrop.action import Action from dotdrop.dotdrop import cmd_install from dotdrop.options import BACKUP_SUFFIX from dotdrop.utils import header from dotdrop.linktypes import LinkTypes class TestInstall(unittest.TestCase): CONFIG_NAME = 'config.yaml' TEMPLATE = ''' # launch the wm {%@@ if profile == "home" @@%} exec awesome {%@@ else @@%} exec bspwm {%@@ endif @@%} ''' RESULT = ''' # launch the wm exec bspwm ''' def fake_config(self, path, dotfiles, profile, dotpath, actions, trans): """Create a fake config file""" with open(path, 'w') as f: f.write('actions:\n') for action in actions: f.write(' {}: {}\n'.format(action.key, action.action)) f.write('trans:\n') for tr in trans: f.write(' {}: {}\n'.format(tr.key, tr.action)) f.write('config:\n') f.write(' backup: true\n') f.write(' create: true\n') f.write(' dotpath: {}\n'.format(dotpath)) f.write('dotfiles:\n') for d in dotfiles: f.write(' {}:\n'.format(d.key)) f.write(' dst: {}\n'.format(d.dst)) f.write(' src: {}\n'.format(d.src)) f.write(' link: {}\n'.format(d.link.name.lower())) if len(d.actions) > 0: f.write(' actions:\n') for action in d.actions: f.write(' - {}\n'.format(action.key)) if d.trans_r: for tr in d.trans_r: f.write(' trans_read: {}\n'.format(tr.key)) f.write('profiles:\n') f.write(' {}:\n'.format(profile)) f.write(' dotfiles:\n') for d in dotfiles: f.write(' - {}\n'.format(d.key)) return path def test_install(self): """Test the install function""" # dotpath location tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) # where dotfiles will be installed dst = get_tempdir() self.assertTrue(os.path.exists(dst)) self.addCleanup(clean, dst) # create the dotfile in dotdrop f1, c1 = create_random_file(tmp) dst1 = os.path.join(dst, get_string(6)) d1 = Dotfile(get_string(5), dst1, os.path.basename(f1)) # fake a __str__ self.assertTrue(str(d1) != '') f2, c2 = create_random_file(tmp) dst2 = os.path.join(dst, get_string(6)) d2 = Dotfile(get_string(5), dst2, os.path.basename(f2)) with open(f2, 'w') as f: f.write(self.TEMPLATE) f3, _ = create_random_file(tmp, binary=True) dst3 = os.path.join(dst, get_string(6)) d3 = Dotfile(get_string(5), dst3, os.path.basename(f3)) # create a directory dotfile dir1 = os.path.join(tmp, 'somedir') create_dir(dir1) fd, _ = create_random_file(dir1) dstd = os.path.join(dst, get_string(6)) ddot = Dotfile(get_string(5), dstd, os.path.basename(dir1)) # to test backup f4, c4 = create_random_file(tmp) dst4 = os.path.join(dst, get_string(6)) d4 = Dotfile(key=get_string(6), dst=dst4, src=os.path.basename(f4)) with open(dst4, 'w') as f: f.write(get_string(16)) # to test link f5, c5 = create_random_file(tmp) dst5 = os.path.join(dst, get_string(6)) self.addCleanup(clean, dst5) d5 = Dotfile(get_string(6), dst5, os.path.basename(f5), link=LinkTypes.LINK) # create the dotfile directories in dotdrop dir1 = create_dir(os.path.join(tmp, get_string(6))) self.assertTrue(os.path.exists(dir1)) self.addCleanup(clean, dir1) dst6 = os.path.join(dst, get_string(6)) # fill with files sub1, _ = create_random_file(dir1, template=True) self.assertTrue(os.path.exists(sub1)) sub2, _ = create_random_file(dir1) self.assertTrue(os.path.exists(sub2)) # make up the dotfile d6 = Dotfile(get_string(6), dst6, os.path.basename(dir1)) # to test symlink directories dir2 = create_dir(os.path.join(tmp, get_string(6))) self.assertTrue(os.path.exists(dir2)) self.addCleanup(clean, dir2) dst7 = os.path.join(dst, get_string(6)) # fill with files sub3, _ = create_random_file(dir2) self.assertTrue(os.path.exists(sub3)) sub4, _ = create_random_file(dir2) self.assertTrue(os.path.exists(sub4)) # make up the dotfile d7 = Dotfile(get_string(6), dst7, os.path.basename(dir2), link=LinkTypes.LINK) # to test actions value = get_string(12) fact = '/tmp/action' self.addCleanup(clean, fact) act1 = Action('testaction', 'post', 'echo "{}" > {}'.format(value, fact)) f8, c8 = create_random_file(tmp) dst8 = os.path.join(dst, get_string(6)) d8 = Dotfile(get_string(6), dst8, os.path.basename(f8), actions=[act1]) # to test transformations trans1 = 'trans1' trans2 = 'trans2' cmd = 'cat {0} | sed \'s/%s/%s/g\' > {1}' % (trans1, trans2) tr = Action('testtrans', 'post', cmd) f9, c9 = create_random_file(tmp, content=trans1) dst9 = os.path.join(dst, get_string(6)) d9 = Dotfile(get_string(6), dst9, os.path.basename(f9), trans_r=[tr]) # to test template f10, _ = create_random_file(tmp, content='{{@@ header() @@}}') dst10 = os.path.join(dst, get_string(6)) d10 = Dotfile(get_string(6), dst10, os.path.basename(f10)) # generate the config and stuff profile = get_string(5) confpath = os.path.join(tmp, self.CONFIG_NAME) dotfiles = [d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, ddot] self.fake_config(confpath, dotfiles, profile, tmp, [act1], [tr]) conf = Cfg(confpath, profile, debug=True) self.assertTrue(conf is not None) # install them o = load_options(confpath, profile) o.safe = False o.install_showdiff = True cmd_install(o) # now compare the generated files self.assertTrue(os.path.exists(dst1)) self.assertTrue(os.path.exists(dst2)) self.assertTrue(os.path.exists(dst3)) self.assertTrue(os.path.exists(dst5)) self.assertTrue(os.path.exists(dst6)) self.assertTrue(os.path.exists(dst7)) self.assertTrue(os.path.exists(dst8)) self.assertTrue(os.path.exists(dst10)) self.assertTrue(os.path.exists(fd)) # check if 'dst5' is a link whose target is 'f5' self.assertTrue(os.path.islink(dst5)) self.assertTrue(os.path.realpath(dst5) == os.path.realpath(f5)) # check if 'dst7' is a link whose target is 'dir2' self.assertTrue(os.path.islink(dst7)) self.assertTrue(os.path.realpath(dst7) == os.path.realpath(dir2)) # make sure backup is there b = dst4 + BACKUP_SUFFIX self.assertTrue(os.path.exists(b)) self.assertTrue(filecmp.cmp(f1, dst1, shallow=True)) f2content = open(dst2, 'r').read() self.assertTrue(f2content == self.RESULT) self.assertTrue(filecmp.cmp(f3, dst3, shallow=True)) # test action has been executed self.assertTrue(os.path.exists(fact)) self.assertTrue(str(act1) != '') actcontent = open(fact, 'r').read().rstrip() self.assertTrue(actcontent == value) # test transformation has been done self.assertTrue(os.path.exists(dst9)) transcontent = open(dst9, 'r').read().rstrip() self.assertTrue(transcontent == trans2) # test template has been remplaced self.assertTrue(os.path.exists(dst10)) tempcontent = open(dst10, 'r').read().rstrip() self.assertTrue(tempcontent == header()) def test_install_import_configs(self): """Test the install function with imported configs""" # dotpath location tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) os.mkdir(os.path.join(tmp, 'importing')) os.mkdir(os.path.join(tmp, 'imported')) # where dotfiles will be installed dst = get_tempdir() self.assertTrue(os.path.exists(dst)) self.addCleanup(clean, dst) # creating random dotfiles imported_dotfile, _ = create_random_file(os.path.join(tmp, 'imported')) imported_dotfile = { 'dst': os.path.join(dst, imported_dotfile), 'key': 'f_{}'.format(imported_dotfile), 'name': imported_dotfile, 'src': os.path.join(tmp, 'imported', imported_dotfile), } importing_dotfile, _ = \ create_random_file(os.path.join(tmp, 'importing')) importing_dotfile = { 'dst': os.path.join(dst, importing_dotfile), 'key': 'f_{}'.format(importing_dotfile), 'name': importing_dotfile, 'src': os.path.join(tmp, 'imported', importing_dotfile), } imported = { 'config': { 'dotpath': 'imported', }, 'dotfiles': { imported_dotfile['key']: { 'dst': imported_dotfile['dst'], 'src': imported_dotfile['name'], }, }, 'profiles': { 'host1': { 'dotfiles': [imported_dotfile['key']], }, }, } importing = { 'config': { 'dotpath': 'importing', }, 'dotfiles': { importing_dotfile['key']: { 'dst': importing_dotfile['dst'], 'src': importing_dotfile['src'], }, }, 'profiles': { 'host2': { 'dotfiles': [importing_dotfile['key']], 'include': ['host1'], }, }, } # create the imported base config file imported_path = create_fake_config(tmp, configname='config-2.yaml', **imported['config']) # create the importing base config file importing_path = create_fake_config(tmp, configname='config.yaml', import_configs=['config-2.yaml'], **importing['config']) # edit the imported config populate_fake_config(imported_path, **{ k: v for k, v in imported.items() if k != 'config' }) # edit the importing config populate_fake_config(importing_path, **{ k: v for k, v in importing.items() if k != 'config' }) # install them o = load_options(importing_path, 'host2') o.safe = False o.install_showdiff = True o.variables = {} cmd_install(o) # now compare the generated files self.assertTrue(os.path.exists(importing_dotfile['dst'])) self.assertTrue(os.path.exists(imported_dotfile['dst'])) def test_link_children(self): """test the link children""" # create source dir src_dir = get_tempdir() self.assertTrue(os.path.exists(src_dir)) self.addCleanup(clean, src_dir) # where dotfiles will be installed dst_dir = get_tempdir() self.assertTrue(os.path.exists(dst_dir)) self.addCleanup(clean, dst_dir) # create 3 random files in source srcs = [create_random_file(src_dir)[0] for _ in range(3)] installer = Installer() installer.install(templater=MagicMock(), src=src_dir, dst=dst_dir, linktype=LinkTypes.LINK_CHILDREN, actionexec=None) # Ensure all destination files point to source for src in srcs: dst = os.path.join(dst_dir, src) self.assertEqual(os.path.realpath(dst), os.path.realpath(src)) def test_fails_without_src(self): """test fails without src""" src = '/some/non/existant/file' installer = Installer() # logger = MagicMock() # installer.log.err = logger res, err = installer.install(templater=MagicMock(), src=src, dst='/dev/null', linktype=LinkTypes.LINK_CHILDREN, actionexec=None) self.assertFalse(res) e = 'source dotfile does not exist: {}'.format(src) self.assertEqual(err, e) def test_fails_when_src_file(self): """test fails when src file""" # create source dir src_dir = get_tempdir() self.assertTrue(os.path.exists(src_dir)) self.addCleanup(clean, src_dir) src = create_random_file(src_dir)[0] # logger = MagicMock() templater = MagicMock() installer = Installer() # installer.log.err = logger # pass src file not src dir res, err = installer.install(templater=templater, src=src, dst='/dev/null', linktype=LinkTypes.LINK_CHILDREN, actionexec=None) # ensure nothing performed self.assertFalse(res) e = 'source dotfile is not a directory: {}'.format(src) self.assertEqual(err, e) def test_creates_dst(self): """test creates dst""" src_dir = get_tempdir() self.assertTrue(os.path.exists(src_dir)) self.addCleanup(clean, src_dir) # where dotfiles will be installed dst_dir = get_tempdir() self.addCleanup(clean, dst_dir) # move dst dir to new (uncreated) dir in dst dst_dir = os.path.join(dst_dir, get_string(6)) self.assertFalse(os.path.exists(dst_dir)) installer = Installer() installer.install(templater=MagicMock(), src=src_dir, dst=dst_dir, linktype=LinkTypes.LINK_CHILDREN, actionexec=None) # ensure dst dir created self.assertTrue(os.path.exists(dst_dir)) def test_prompts_to_replace_dst(self): """test prompts to replace dst""" # create source dir src_dir = get_tempdir() self.assertTrue(os.path.exists(src_dir)) self.addCleanup(clean, src_dir) # where dotfiles will be installed dst_dir = get_tempdir() self.addCleanup(clean, dst_dir) # Create destination file to be replaced dst = os.path.join(dst_dir, get_string(6)) with open(dst, 'w'): pass self.assertTrue(os.path.isfile(dst)) # setup mocks ask = MagicMock() ask.return_value = True # setup installer installer = Installer() installer.safe = True installer.log.ask = ask installer.install(templater=MagicMock(), src=src_dir, dst=dst, linktype=LinkTypes.LINK_CHILDREN, actionexec=None) # ensure destination now a directory self.assertTrue(os.path.isdir(dst)) # ensure prompted ask.assert_called_with( 'Remove regular file {} and replace with empty directory?' .format(dst)) def test_runs_templater(self): """test runs templater""" # create source dir src_dir = get_tempdir() self.assertTrue(os.path.exists(src_dir)) self.addCleanup(clean, src_dir) # where dotfiles will be installed dst_dir = get_tempdir() self.assertTrue(os.path.exists(dst_dir)) self.addCleanup(clean, dst_dir) # create 3 random files in source srcs = [create_random_file(src_dir)[0] for _ in range(3)] # setup installer and mocks installer = Installer() templater = MagicMock() templater.generate.return_value = b'content' installer.install(templater=templater, src=src_dir, dst=dst_dir, linktype=LinkTypes.LINK_CHILDREN, actionexec=None) for src in srcs: dst = os.path.join(dst_dir, os.path.basename(src)) # ensure dst is link self.assertTrue(os.path.islink(dst)) # ensure dst not directly linked to src self.assertNotEqual(os.path.realpath(dst), src) def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_jhelpers.py000066400000000000000000000061151436430751200176560ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 basic unittest for jhelpers """ import os import unittest from dotdrop.cfg_aggregator import CfgAggregator from tests.helpers import (clean, create_random_file, get_string, get_tempdir, load_options) from dotdrop.dotfile import Dotfile from dotdrop.dotdrop import cmd_install class TestJhelpers(unittest.TestCase): CONFIG_NAME = 'config.yaml' TEMPLATE = ''' {%@@ if exists('/dev/null') @@%} it does not exist {%@@ endif @@%} {%@@ if exists('/tmp') @@%} it does exist {%@@ endif @@%} {%@@ if exists_in_path('ls') @@%} ls exists {%@@ endif @@%} {%@@ if not exists_in_path('itdoesnotexist') @@%} itdoesnotexist does not exist {%@@ endif @@%} {%@@ set the_basename = basename('/tmp/a/b/c') @@%} basename: {{@@ the_basename @@}} {%@@ set the_dirname = dirname('/tmp/a/b/c') @@%} dirname: {{@@ the_dirname @@}} ''' RESULT = ''' it does not exist it does exist ls exists itdoesnotexist does not exist basename: c dirname: /tmp/a/b ''' def fake_config(self, path, dotfile, profile, dotpath): """Create a fake config file""" with open(path, 'w') as f: f.write('config:\n') f.write(' backup: true\n') f.write(' create: true\n') f.write(' dotpath: {}\n'.format(dotpath)) f.write('dotfiles:\n') f.write(' {}:\n'.format(dotfile.key)) f.write(' dst: {}\n'.format(dotfile.dst)) f.write(' src: {}\n'.format(dotfile.src)) f.write('profiles:\n') f.write(' {}:\n'.format(profile)) f.write(' dotfiles:\n') f.write(' - {}\n'.format(dotfile.key)) return path def test_jhelpers(self): """Test the install function""" # dotpath location tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) # where dotfiles will be installed dst = get_tempdir() self.assertTrue(os.path.exists(dst)) self.addCleanup(clean, dst) # create the dotfile in dotdrop f1, c1 = create_random_file(tmp) with open(f1, 'w') as f: f.write(self.TEMPLATE) dst1 = os.path.join(dst, get_string(6)) d1 = Dotfile(get_string(5), dst1, os.path.basename(f1)) # generate the config and stuff profile = get_string(5) confpath = os.path.join(tmp, self.CONFIG_NAME) self.fake_config(confpath, d1, profile, tmp) conf = CfgAggregator(confpath, profile, debug=True) self.assertTrue(conf is not None) # install them o = load_options(confpath, profile) o.safe = False o.install_showdiff = True o.variables = {} o.debug = True cmd_install(o) # now compare the generated files self.assertTrue(os.path.exists(dst1)) f1content = open(dst1, 'r').read() self.assertTrue(f1content == self.RESULT) def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_listings.py000066400000000000000000000061551436430751200177020ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the compare function """ import unittest import os from dotdrop.dotdrop import cmd_list_profiles from dotdrop.dotdrop import cmd_files from dotdrop.dotdrop import cmd_detail from dotdrop.dotdrop import cmd_importer from tests.helpers import create_dir, get_string, get_tempdir, \ create_random_file, load_options, \ create_fake_config, clean class TestListings(unittest.TestCase): """listing test""" CONFIG_BACKUP = False CONFIG_CREATE = True CONFIG_DOTPATH = 'dotfiles' CONFIG_NAME = 'config.yaml' def test_listings(self): """Test the compare function""" # setup some directories fold_config = os.path.join(os.path.expanduser('~'), '.config') create_dir(fold_config) fold_subcfg = os.path.join(os.path.expanduser('~'), '.config', get_string(5)) create_dir(fold_subcfg) self.addCleanup(clean, fold_subcfg) fold_tmp = get_tempdir() create_dir(fold_tmp) self.addCleanup(clean, fold_tmp) # create the directories tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) dotfilespath = get_tempdir() self.assertTrue(os.path.exists(dotfilespath)) self.addCleanup(clean, dotfilespath) # create the dotfiles to test d1, c1 = create_random_file(fold_config) self.assertTrue(os.path.exists(d1)) self.addCleanup(clean, d1) d2, c2 = create_random_file(fold_subcfg) self.assertTrue(os.path.exists(d2)) self.addCleanup(clean, d2) d3, c3 = create_random_file(fold_tmp) self.assertTrue(os.path.exists(d3)) self.addCleanup(clean, d3) d4, c4 = create_random_file(fold_tmp, binary=True) self.assertTrue(os.path.exists(d4)) self.addCleanup(clean, d4) d5 = get_tempdir() self.assertTrue(os.path.exists(d5)) self.addCleanup(clean, d5) d6, _ = create_random_file(d5) self.assertTrue(os.path.exists(d6)) # create the config file profile = get_string(5) confpath = create_fake_config(dotfilespath, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) self.assertTrue(os.path.exists(confpath)) o = load_options(confpath, profile) dfiles = [d1, d2, d3, d4, d5] # import the files o.import_path = dfiles cmd_importer(o) o = load_options(confpath, profile) # files cmd_list_profiles(o) # list files o.files_templateonly = False cmd_files(o) o.files_templateonly = True cmd_files(o) # details o.detail_keys = None cmd_detail(o) def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_remove.py000066400000000000000000000076141436430751200173440ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2019, deadc0de6 basic unittest for the remove function """ import unittest import os # local imports from dotdrop.dotdrop import cmd_remove from tests.helpers import (clean, create_dir, create_random_file, load_options, get_tempdir, yaml_load, yaml_dump) class TestRemove(unittest.TestCase): def load_yaml(self, path): """Load yaml to dict""" self.assertTrue(os.path.exists(path)) return yaml_load(path) def test_remove(self): """test the remove command""" # dotfiles in dotpath dotdrop_home = get_tempdir() self.assertTrue(os.path.exists(dotdrop_home)) self.addCleanup(clean, dotdrop_home) dotfilespath = os.path.join(dotdrop_home, 'dotfiles') confpath = os.path.join(dotdrop_home, 'config.yaml') create_dir(dotfilespath) df1, _ = create_random_file(dotfilespath) df2, _ = create_random_file(dotfilespath) df3, _ = create_random_file(dotfilespath) configdic = { 'config': { 'dotpath': 'dotfiles', }, 'dotfiles': { 'f_test1': { 'src': df1, 'dst': '/dev/null' }, 'f_test2': { 'src': df2, 'dst': '/dev/null' }, 'f_test3': { 'src': df3, 'dst': '/tmp/some-fake-path' }, }, 'profiles': { 'host1': { 'dotfiles': ['f_test1', 'f_test2', 'f_test3'], }, 'host2': { 'dotfiles': ['f_test1'], }, 'host3': { 'dotfiles': ['f_test2'], }, }, } yaml_dump(configdic, confpath) o = load_options(confpath, 'host1') o.remove_path = ['f_test1'] o.remove_iskey = True o.debug = True o.safe = False # by key cmd_remove(o) # ensure file is deleted self.assertFalse(os.path.exists(df1)) self.assertTrue(os.path.exists(df2)) self.assertTrue(os.path.exists(df3)) # load dict y = yaml_load(confpath) # ensure not present self.assertTrue('f_test1' not in y['dotfiles']) self.assertTrue('f_test1' not in y['profiles']['host1']['dotfiles']) self.assertTrue('host2' not in y['profiles']) # assert rest is intact self.assertTrue('f_test2' in y['dotfiles'].keys()) self.assertTrue('f_test3' in y['dotfiles'].keys()) self.assertTrue('f_test2' in y['profiles']['host1']['dotfiles']) self.assertTrue('f_test3' in y['profiles']['host1']['dotfiles']) self.assertTrue(y['profiles']['host3']['dotfiles'] == ['f_test2']) o = load_options(confpath, 'host1') o.remove_path = ['/tmp/some-fake-path'] o.remove_iskey = False o.debug = True o.safe = False # by path cmd_remove(o) # ensure file is deleted self.assertTrue(os.path.exists(df2)) self.assertFalse(os.path.exists(df3)) # load dict y = yaml_load(confpath) # ensure not present self.assertTrue('f_test3' not in y['dotfiles']) self.assertTrue('f_test3' not in y['profiles']['host1']['dotfiles']) # assert rest is intact self.assertTrue('host1' in y['profiles'].keys()) self.assertFalse('host2' in y['profiles'].keys()) self.assertTrue('host3' in y['profiles'].keys()) self.assertTrue(y['profiles']['host1']['dotfiles'] == ['f_test2']) self.assertTrue(y['profiles']['host3']['dotfiles'] == ['f_test2']) def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_update.py000066400000000000000000000133701436430751200173250ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the update function """ import unittest import os from dotdrop.dotdrop import cmd_update from dotdrop.dotdrop import cmd_importer from dotdrop.action import Transform from tests.helpers import create_dir, get_string, get_tempdir, clean, \ create_random_file, create_fake_config, load_options, edit_content class TestUpdate(unittest.TestCase): CONFIG_BACKUP = False CONFIG_CREATE = True CONFIG_DOTPATH = 'dotfiles' CONFIG_NAME = 'config.yaml' def test_update(self): """Test the update function""" # setup some directories fold_config = os.path.join(os.path.expanduser('~'), '.config') create_dir(fold_config) fold_subcfg = os.path.join(os.path.expanduser('~'), '.config', get_string(5)) create_dir(fold_subcfg) self.addCleanup(clean, fold_subcfg) fold_tmp = get_tempdir() create_dir(fold_tmp) self.addCleanup(clean, fold_tmp) # create the directories tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) dotfilespath = get_tempdir() self.assertTrue(os.path.exists(dotfilespath)) self.addCleanup(clean, dotfilespath) # create the dotfiles to test d1, c1 = create_random_file(fold_config) self.assertTrue(os.path.exists(d1)) self.addCleanup(clean, d1) d2, c2 = create_random_file(fold_config) self.assertTrue(os.path.exists(d2)) self.addCleanup(clean, d2) # template d3t, c3t = create_random_file(fold_config) self.assertTrue(os.path.exists(d3t)) self.addCleanup(clean, d3t) # sub dirs dsubstmp = get_tempdir() self.assertTrue(os.path.exists(dsubstmp)) self.addCleanup(clean, dsubstmp) dirsubs = os.path.basename(dsubstmp) dir1string = 'somedir' dir1 = os.path.join(dsubstmp, dir1string) create_dir(dir1) dir1sub1str = 'sub1' sub1 = os.path.join(dir1, dir1sub1str) create_dir(sub1) dir1sub2str = 'sub2' sub2 = os.path.join(dir1, dir1sub2str) create_dir(sub2) f1s1, f1s1c1 = create_random_file(sub1) self.assertTrue(os.path.exists(f1s1)) f1s2, f1s2c1 = create_random_file(sub2) self.assertTrue(os.path.exists(f1s2)) # create the directory to test dpath = os.path.join(fold_config, get_string(5)) dir1 = create_dir(dpath) dirf1, _ = create_random_file(dpath) self.addCleanup(clean, dir1) # create the config file profile = get_string(5) confpath = create_fake_config(dotfilespath, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) self.assertTrue(os.path.exists(confpath)) o = load_options(confpath, profile) o.update_showpatch = True dfiles = [d1, dir1, d2, d3t, dsubstmp] # import the files o.import_path = dfiles cmd_importer(o) # get new config o = load_options(confpath, profile) o.safe = False o.update_showpatch = True o.debug = True trans = Transform('trans', 'cp -r {0} {1}') d3tb = os.path.basename(d3t) for dotfile in o.dotfiles: if os.path.basename(dotfile.dst) == d3tb: # patch the template src = os.path.join(o.dotpath, dotfile.src) src = os.path.expanduser(src) edit_content(src, '{{@@ profile @@}}') left = os.path.realpath(os.path.basename(dotfile.dst)) right = os.path.realpath(dirsubs) if left == right: # retrieve the path of the sub in the dotpath d1indotpath = os.path.join(o.dotpath, dotfile.src) d1indotpath = os.path.expanduser(d1indotpath) dotfile.trans_w = trans # update template o.update_path = [d3t] self.assertFalse(cmd_update(o)) # update sub dirs gone = os.path.join(d1indotpath, dir1string) gone = os.path.join(gone, dir1sub1str) self.assertTrue(os.path.exists(gone)) clean(sub1) # dir1sub1str self.assertTrue(os.path.exists(gone)) o.update_path = [dsubstmp] cmd_update(o) self.assertFalse(os.path.exists(gone)) # edit the files edit_content(d1, 'newcontent') edit_content(dirf1, 'newcontent') # add more file dirf2, _ = create_random_file(dpath) # add more dirs dpath = os.path.join(dpath, get_string(5)) create_dir(dpath) create_random_file(dpath) # update it o.update_path = [d1, dir1] cmd_update(o) # test content newcontent = open(d1, 'r').read() self.assertTrue(newcontent == 'newcontent') newcontent = open(dirf1, 'r').read() self.assertTrue(newcontent == 'newcontent') edit_content(d2, 'newcontentbykey') # update it by key dfiles = o.dotfiles d2key = '' for ds in dfiles: t = os.path.expanduser(ds.dst) if t == d2: d2key = ds.key break self.assertTrue(d2key != '') o.update_path = [d2key] o.update_iskey = True cmd_update(o) # test content newcontent = open(d2, 'r').read() self.assertTrue(newcontent == 'newcontentbykey') def main(): unittest.main() if __name__ == '__main__': main() dotdrop-1.12.9/tests/test_yamlcfg.py000066400000000000000000000465631436430751200174770ustar00rootroot00000000000000""" author: deadc0de6 (https://github.com/deadc0de6) Copyright (c) 2017, deadc0de6 basic unittest for the config parser """ import unittest from unittest.mock import patch import os from dotdrop.cfg_yaml import CfgYaml as Cfg from dotdrop.options import Options from dotdrop.linktypes import LinkTypes from dotdrop.exceptions import YamlException from tests.helpers import (SubsetTestCase, _fake_args, clean, create_fake_config, create_yaml_keyval, get_tempdir, populate_fake_config, yaml_load, yaml_dump) class TestConfig(SubsetTestCase): CONFIG_BACKUP = False CONFIG_CREATE = True CONFIG_DOTPATH = 'dotfiles' PROFILE = 'p1' TMPSUFFIX = '.dotdrop' CONFIG_NAME = 'config.yaml' CONFIG_NAME_2 = 'config-2.yaml' def test_config(self): """Test the config class""" tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) confpath = create_fake_config(tmp, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) conf = Cfg(confpath, self.PROFILE, debug=True) self.assertTrue(conf is not None) opts = conf.settings self.assertTrue(opts is not None) self.assertTrue(opts != {}) self.assertTrue(opts['backup'] == self.CONFIG_BACKUP) self.assertTrue(opts['create'] == self.CONFIG_CREATE) dpath = os.path.basename(opts['dotpath']) self.assertTrue(dpath == self.CONFIG_DOTPATH) self.assertTrue(conf.dump() != '') def test_def_link(self): self._test_link_import('nolink', LinkTypes.ABSOLUTE, 'absolute') self._test_link_import('nolink', LinkTypes.RELATIVE, 'relative') self._test_link_import('nolink', LinkTypes.NOLINK, 'nolink') self._test_link_import('nolink', LinkTypes.LINK_CHILDREN, 'link_children') self._test_link_import('link', LinkTypes.ABSOLUTE, 'absolute') self._test_link_import('link', LinkTypes.RELATIVE, 'relative') self._test_link_import('link', LinkTypes.NOLINK, 'nolink') self._test_link_import('link', LinkTypes.LINK_CHILDREN, 'link_children') self._test_link_import('link_children', LinkTypes.ABSOLUTE, 'absolute') self._test_link_import('link_children', LinkTypes.RELATIVE, 'relative') self._test_link_import('link_children', LinkTypes.NOLINK, 'nolink') self._test_link_import('link_children', LinkTypes.LINK_CHILDREN, 'link_children') self._test_link_import_fail('whatever') @patch('dotdrop.cfg_yaml.open', create=True) @patch('dotdrop.cfg_yaml.os.path.exists', create=True) def _test_link_import(self, cfgstring, expected, cliargs, mock_exists, mock_open): data = ''' config: backup: true create: true dotpath: dotfiles banner: true longkey: false keepdot: false link_on_import: {} link_dotfile_default: nolink dotfiles: profiles: '''.format(cfgstring) mock_open.side_effect = [ unittest.mock.mock_open(read_data=data).return_value, unittest.mock.mock_open(read_data=data).return_value ] mock_exists.return_value = True args = _fake_args() args['--profile'] = self.PROFILE args['--cfg'] = 'mocked' args['--link'] = cliargs args['--verbose'] = True o = Options(args=args) self.assertTrue(o.import_link == expected) @patch('dotdrop.cfg_yaml.open', create=True) @patch('dotdrop.cfg_yaml.os.path.exists', create=True) def _test_link_import_fail(self, value, mock_exists, mock_open): data = ''' config: backup: true create: true dotpath: dotfiles banner: true longkey: false keepdot: false link_on_import: {} link_dotfile_default: nolink dotfiles: profiles: '''.format(value) mock_open.side_effect = [ unittest.mock.mock_open(read_data=data).return_value ] mock_exists.return_value = True args = _fake_args() args['--profile'] = 'p1' args['--cfg'] = 'mocked' args['--verbose'] = True with self.assertRaises(YamlException): o = Options(args=args) print(o.import_link) def test_include(self): tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) # create a base config file confpath = create_fake_config(tmp, configname=self.CONFIG_NAME, dotpath=self.CONFIG_DOTPATH, backup=self.CONFIG_BACKUP, create=self.CONFIG_CREATE) # edit the config content = yaml_load(confpath) # adding dotfiles df1key = 'f_vimrc' df2key = 'f_xinitrc' content['dotfiles'] = { df1key: {'dst': '~/.vimrc', 'src': 'vimrc'}, df2key: {'dst': '~/.xinitrc', 'src': 'xinitrc'} } # adding profiles pf1key = 'host1' pf2key = 'host2' content['profiles'] = { pf1key: {'dotfiles': [df2key], 'include': ['host2']}, pf2key: {'dotfiles': [df1key]} } # save the new config yaml_dump(content, confpath) # do the tests conf = Cfg(confpath, debug=True) self.assertTrue(conf is not None) # test profile profiles = conf.profiles self.assertTrue(pf1key in profiles) self.assertTrue(pf2key in profiles) # test dotfiles dotfiles = conf.profiles[pf1key]['dotfiles'] self.assertTrue(df1key in dotfiles) self.assertTrue(df2key in dotfiles) dotfiles = conf.profiles[pf2key]['dotfiles'] self.assertTrue(df1key in dotfiles) self.assertFalse(df2key in dotfiles) # test not existing included profile # edit the config content = yaml_load(confpath) content['profiles'] = { pf1key: {'dotfiles': [df2key], 'include': ['host2']}, pf2key: {'dotfiles': [df1key], 'include': ['host3']} } # save the new config yaml_dump(content, confpath) # do the tests conf = Cfg(confpath, debug=True) self.assertTrue(conf is not None) def test_import_configs_merge(self): """Test import_configs when all config keys merge.""" tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) vars_ed = { 'variables': { 'a_var_ed': '33', }, 'dynvariables': { 'a_dynvar_ed': 'echo 33', }, } vars_ing = { 'variables': { 'a_var_ing': 'dd', }, 'dynvariables': { 'a_dynvar_ing': 'echo dd', }, } vars_ed_file = create_yaml_keyval(vars_ed, tmp) vars_ing_file = create_yaml_keyval(vars_ing, tmp) actions_ed = { 'actions': { 'pre': { 'a_pre_action_ed': 'echo pre 22', }, 'post': { 'a_post_action_ed': 'echo post 22', }, 'a_action_ed': 'echo 22', } } actions_ing = { 'actions': { 'pre': { 'a_pre_action_ing': 'echo pre aa', }, 'post': { 'a_post_action_ing': 'echo post aa', }, 'a_action_ing': 'echo aa', } } actions_ed_file = create_yaml_keyval(actions_ed, tmp) actions_ing_file = create_yaml_keyval(actions_ing, tmp) imported = { 'config': { 'dotpath': 'importing', 'import_variables': [vars_ed_file], 'import_actions': [actions_ed_file], }, 'dotfiles': { 'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'}, }, 'profiles': { 'host1': { 'dotfiles': ['f_vimrc'], }, }, 'actions': { 'pre': { 'a_pre_log_ed': 'echo pre 2', }, 'post': { 'a_post_log_ed': 'echo post 2', }, 'a_log_ed': 'echo 2', }, 'trans': { 't_log_ed': 'echo 3', }, 'trans_write': { 'tw_log_ed': 'echo 4', }, 'variables': { 'v_log_ed': '42', }, 'dynvariables': { 'dv_log_ed': 'echo 5', }, } importing = { 'config': { 'dotpath': 'importing', 'import_variables': [vars_ing_file], 'import_actions': [actions_ing_file], }, 'dotfiles': { 'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'}, }, 'profiles': { 'host2': { 'dotfiles': ['f_xinitrc'], 'include': ['host1'], }, }, 'actions': { 'pre': { 'a_pre_log_ing': 'echo pre a', }, 'post': { 'a_post_log_ing': 'echo post a', }, 'a_log_ing': 'echo a', }, 'trans': { 't_log_ing': 'echo b', }, 'trans_write': { 'tw_log_ing': 'echo c', }, 'variables': { 'v_log_ing': 'd', }, 'dynvariables': { 'dv_log_ing': 'echo e', }, } # create the imported base config file imported_path = create_fake_config(tmp, configname=self.CONFIG_NAME_2, **imported['config']) # create the importing base config file importing_path = create_fake_config(tmp, configname=self.CONFIG_NAME, import_configs=[ self.CONFIG_NAME_2 ], **importing['config']) # edit the imported config populate_fake_config(imported_path, **{ k: v for k, v in imported.items() if k != 'config' }) # edit the importing config populate_fake_config(importing_path, **{ k: v for k, v in importing.items() if k != 'config' }) # do the tests importing_cfg = Cfg(importing_path, debug=True) imported_cfg = Cfg(imported_path, debug=True) self.assertIsNotNone(importing_cfg) self.assertIsNotNone(imported_cfg) # test profiles self.assertIsSubset(imported_cfg.profiles, importing_cfg.profiles) # test dotfiles self.assertIsSubset(imported_cfg.dotfiles, importing_cfg.dotfiles) # test actions pre_ed = post_ed = pre_ing = post_ing = {} for k, v in imported_cfg.actions.items(): kind, _ = v if kind == 'pre': pre_ed[k] = v elif kind == 'post': post_ed[k] = v for k, v in importing_cfg.actions.items(): kind, _ = v if kind == 'pre': pre_ing[k] = v elif kind == 'post': post_ing[k] = v self.assertIsSubset(pre_ed, pre_ing) self.assertIsSubset(post_ed, post_ing) # test transactions self.assertIsSubset(imported_cfg.trans_r, importing_cfg.trans_r) self.assertIsSubset(imported_cfg.trans_w, importing_cfg.trans_w) # test variables imported_vars = { k: v for k, v in imported_cfg.variables.items() if not k.startswith('_') } importing_vars = { k: v for k, v in importing_cfg.variables.items() if not k.startswith('_') } self.assertIsSubset(imported_vars, importing_vars) # test prodots self.assertIsSubset(imported_cfg.profiles, importing_cfg.profiles) def test_import_configs_override(self): """Test import_configs when some config keys overlap.""" tmp = get_tempdir() self.assertTrue(os.path.exists(tmp)) self.addCleanup(clean, tmp) vars_ed = { 'variables': { 'a_var': '33', }, 'dynvariables': { 'a_dynvar': 'echo 33', }, } vars_ing = { 'variables': { 'a_var': 'dd', }, 'dynvariables': { 'a_dynvar': 'echo dd', }, } vars_ed_file = create_yaml_keyval(vars_ed, tmp) vars_ing_file = create_yaml_keyval(vars_ing, tmp) actions_ed = { 'actions': { 'pre': { 'a_pre_action': 'echo pre 22', }, 'post': { 'a_post_action': 'echo post 22', }, 'a_action': 'echo 22', } } actions_ing = { 'actions': { 'pre': { 'a_pre_action': 'echo pre aa', }, 'post': { 'a_post_action': 'echo post aa', }, 'a_action': 'echo aa', } } actions_ed_file = create_yaml_keyval(actions_ed, tmp) actions_ing_file = create_yaml_keyval(actions_ing, tmp) imported = { 'config': { 'dotpath': 'imported', 'backup': False, 'import_variables': [vars_ed_file], 'import_actions': [actions_ed_file], }, 'dotfiles': { 'f_vimrc': {'dst': '~/.vimrc', 'src': 'vimrc'}, 'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc', 'link': 'link'}, }, 'profiles': { 'host1': { 'dotfiles': ['f_vimrc'], }, 'host2': { 'dotfiles': ['f_xinitrc'], }, }, 'actions': { 'pre': { 'a_pre_log': 'echo pre 2', }, 'post': { 'a_post_log': 'echo post 2', }, 'a_log': 'echo 2', }, 'trans': { 't_log': 'echo 3', }, 'trans_write': { 'tw_log': 'echo 4', }, 'variables': { 'v_log': '42', }, 'dynvariables': { 'dv_log': 'echo 5', }, } importing = { 'config': { 'dotpath': 'importing', 'backup': True, 'import_variables': [vars_ing_file], 'import_actions': [actions_ing_file], }, 'dotfiles': { 'f_xinitrc': {'dst': '~/.xinitrc', 'src': 'xinitrc'}, }, 'profiles': { 'host2': { 'dotfiles': ['f_xinitrc'], 'include': ['host1'], }, }, 'actions': { 'pre': { 'a_pre_log': 'echo pre a', }, 'post': { 'a_post_log': 'echo post a', }, 'a_log': 'echo a', }, 'trans': { 't_log': 'echo b', }, 'trans_write': { 'tw_log': 'echo c', }, 'variables': { 'v_log': 'd', }, 'dynvariables': { 'dv_log': 'echo e', }, } # create the imported base config file imported_path = create_fake_config(tmp, configname=self.CONFIG_NAME_2, **imported['config']) # create the importing base config file importing_path = create_fake_config(tmp, configname=self.CONFIG_NAME, import_configs=(imported_path,), **importing['config']) # edit the imported config populate_fake_config(imported_path, **{ k: v for k, v in imported.items() if k != 'config' }) # edit the importing config populate_fake_config(importing_path, **{ k: v for k, v in importing.items() if k != 'config' }) # do the tests importing_cfg = Cfg(importing_path, debug=True) imported_cfg = Cfg(imported_path, debug=True) self.assertIsNotNone(importing_cfg) self.assertIsNotNone(imported_cfg) # test profiles self.assertIsSubset(imported_cfg.profiles, importing_cfg.profiles) # test dotfiles self.assertEqual(importing_cfg.dotfiles['f_vimrc'], imported_cfg.dotfiles['f_vimrc']) self.assertNotEqual(importing_cfg.dotfiles['f_xinitrc'], imported_cfg.dotfiles['f_xinitrc']) # test actions self.assertFalse(any( (imported_cfg.actions[key] == importing_cfg.actions[key]) for key in imported_cfg.actions )) # test transactions self.assertFalse(any( imported_cfg.trans_r[key] == importing_cfg.trans_r[key] for key in imported_cfg.trans_r )) self.assertFalse(any( imported_cfg.trans_w[key] == importing_cfg.trans_w[key] for key in imported_cfg.trans_w )) # test variables # since variables get merged they are # the same in both configs imported_vars = imported_cfg.variables self.assertFalse(any( imported_vars[k] != v for k, v in importing_cfg.variables.items() if not k.startswith('_') )) # test profiles dotfiles self.assertEqual(imported_cfg.profiles['host1']['dotfiles'], importing_cfg.profiles['host1']['dotfiles']) self.assertNotEqual(imported_cfg.profiles['host2']['dotfiles'], importing_cfg.profiles['host2']['dotfiles']) self.assertTrue(set(imported_cfg.profiles['host1']['dotfiles']) < set(importing_cfg.profiles['host2']['dotfiles'])) def main(): unittest.main() if __name__ == '__main__': main()