[
  {
    "path": ".ansible-lint",
    "content": "---\nexclude_paths:\n  - galaxy-deploy.yml\n  - .ansible/roles/*\nskip_list:\n  - risky-file-permissions\n  - yaml\n  - fqcn-builtins\n  - fqcn[action]\n  - name[template]\n  - var-naming[no-role-prefix]\n  - no-changed-when\n  - galaxy[no-changelog]\n"
  },
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n---\ngithub: geerlingguy\npatreon: geerlingguy\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "# Configuration for probot-stale - https://github.com/probot/stale\n\n# Number of days of inactivity before an Issue or Pull Request becomes stale\ndaysUntilStale: 90\n\n# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.\n# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.\ndaysUntilClose: 30\n\n# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)\nonlyLabels: []\n\n# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable\nexemptLabels:\n  - bug\n  - pinned\n  - security\n  - planned\n\n# Set to true to ignore issues in a project (defaults to false)\nexemptProjects: false\n\n# Set to true to ignore issues in a milestone (defaults to false)\nexemptMilestones: false\n\n# Set to true to ignore issues with an assignee (defaults to false)\nexemptAssignees: false\n\n# Label to use when marking as stale\nstaleLabel: stale\n\n# Limit the number of actions per hour, from 1-30. Default is 30\nlimitPerRun: 30\n\npulls:\n  markComment: |-\n    This pull request has been marked 'stale' due to lack of recent activity. If there is no further activity, the PR will be closed in another 30 days. Thank you for your contribution!\n\n    Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark pull requests as stale.\n\n  unmarkComment: >-\n    This pull request is no longer marked for closure.\n\n  closeComment: >-\n    This pull request has been closed due to inactivity. If you feel this is in error, please reopen the pull request or file a new PR with the relevant details.\n\nissues:\n  markComment: |-\n    This issue has been marked 'stale' due to lack of recent activity. If there is no further activity, the issue will be closed in another 30 days. Thank you for your contribution!\n\n    Please read [this blog post](https://www.jeffgeerling.com/blog/2020/enabling-stale-issue-bot-on-my-github-repositories) to see the reasons why I mark issues as stale.\n\n  unmarkComment: >-\n    This issue is no longer marked for closure.\n\n  closeComment: >-\n    This issue has been closed due to inactivity. If you feel this is in error, please reopen the issue or file a new issue with the relevant details.\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "---\nname: CI\n'on':\n  pull_request:\n  push:\n    branches:\n      - master\n  schedule:\n    - cron: \"0 5 * * 0\"\n\ndefaults:\n  run:\n    working-directory: ansible_collections/geerlingguy/mac\n\njobs:\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check out the codebase.\n        uses: actions/checkout@v4\n        with:\n          path: ansible_collections/geerlingguy/mac\n\n      - name: Set up Python 3.\n        uses: actions/setup-python@v5\n        with:\n          python-version: '3.x'\n\n      - name: Install test dependencies.\n        run: pip3 install yamllint ansible-core ansible-lint\n\n      - name: Lint code.\n        run: |\n          yamllint .\n          ansible-lint\n\n  integration:\n    name: Integration\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os:\n          - macos-latest\n\n    steps:\n      - name: Check out the codebase.\n        uses: actions/checkout@v4\n        with:\n          path: ansible_collections/geerlingguy/mac\n\n      - name: Uninstall GitHub Actions' built-in Homebrew.\n        run: tests/uninstall-homebrew.sh\n\n      - name: Uninstall GitHub Actions' built-in browser installs.\n        run: |\n          sudo rm -rf /Applications/Firefox.app\n          sudo rm -rf /Applications/Google\\ Chrome.app\n\n      - name: Install test dependencies.\n        run: |\n          sudo pip3 install --upgrade pip\n          sudo pip3 install ansible-core\n\n      - name: Set up the test environment.\n        run: |\n          cp tests/ansible.cfg ./ansible.cfg\n          cp tests/inventory ./inventory\n          ansible-galaxy collection install community.general\n          ansible-galaxy role install elliotweiser.osx-command-line-tools\n\n      - name: Test the playbook's syntax.\n        run: ansible-playbook tests/test.yml --syntax-check\n\n      - name: Test the playbook.\n        run: ansible-playbook tests/test.yml\n        env:\n          ANSIBLE_FORCE_COLOR: '1'\n\n      - name: Idempotence check.\n        run: |\n          idempotence=$(mktemp)\n          ansible-playbook tests/test.yml | tee -a ${idempotence}\n          tail ${idempotence} | grep -q 'changed=0.*failed=0' && (echo 'Idempotence test: pass' && exit 0) || (echo 'Idempotence test: fail' && exit 1)\n        env:\n          ANSIBLE_FORCE_COLOR: '1'\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "---\nname: Release\n'on':\n  push:\n    tags:\n      - '*'\n\ndefaults:\n  run:\n    working-directory: ansible_collections/geerlingguy/mac\n\njobs:\n\n  release:\n    name: Release\n    runs-on: ubuntu-latest\n    env:\n      ANSIBLE_GALAXY_TOKEN: ${{ secrets.ANSIBLE_GALAXY_TOKEN }}\n      ANSIBLE_FORCE_COLOR: 1\n\n    steps:\n      - name: Check out the codebase.\n        uses: actions/checkout@v2\n        with:\n          path: ansible_collections/geerlingguy/mac\n\n      - name: Set up Python 3.\n        uses: actions/setup-python@v2\n        with:\n          python-version: '3.x'\n\n      - name: Install Ansible.\n        run: pip3 install ansible-core\n\n      - name: Release to Ansible Galaxy.\n        run: ansible-playbook -i 'localhost,' galaxy-deploy.yml -e \"github_tag=${{ github.ref }}\"\n"
  },
  {
    "path": ".gitignore",
    "content": "roles/.DS_Store\n"
  },
  {
    "path": ".yamllint",
    "content": "---\nextends: default\n\nrules:\n  line-length:\n    max: 180\n    level: warning\n  comments:\n    min-spaces-from-content: 1\n  comments-indentation: false\n  braces:\n    min-spaces-inside: 0\n    max-spaces-inside: 1\n  octal-values:\n    forbid-implicit-octal: true\n    forbid-explicit-octal: true\n\nignore: |\n  .github/stale.yml\n"
  },
  {
    "path": "LICENSE",
    "content": "Copyright 2021 Jeff Geerling\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Mac Collection for Ansible\n\n[![MIT licensed][badge-license]][link-license]\n[![Galaxy Collection][badge-collection]][link-galaxy]\n[![CI][badge-gh-actions]][link-gh-actions]\n\nThis collection includes helpful Ansible roles and content to help with macOS automation. For a good example of the collection's usage, see the [Mac Dev Playbook](https://github.com/geerlingguy/mac-dev-playbook).\n\nRoles included in this collection (click on the link to see the role's README and documentation):\n\n  - `geerlingguy.mac.homebrew` ([documentation](https://github.com/geerlingguy/ansible-collection-mac/blob/master/roles/homebrew/README.md))\n  - `geerlingguy.mac.mas` ([documentation](https://github.com/geerlingguy/ansible-collection-mac/blob/master/roles/mas/README.md))\n  - `geerlingguy.mac.dock` ([documentation](https://github.com/geerlingguy/ansible-collection-mac/blob/master/roles/dock/README.md))\n\n## Installation\n\nInstall via Ansible Galaxy:\n\n```\nansible-galaxy collection install geerlingguy.mac\n```\n\nOr include this collection in your playbook's `requirements.yml` file:\n\n```\n---\ncollections:\n  - name: geerlingguy.mac\n```\n\nFor a real-world example, see my [Mac Dev Playbook's requirements file](https://github.com/geerlingguy/mac-dev-playbook/blob/master/requirements.yml).\n\n### Role Requirements\n\nRequires separate installation of the `elliotweiser.osx-command-line-tools` role. Because Ansible collections are not able to depend on roles, you will need to make sure that role is installed either by manually installing it with the `ansible-galaxy` command, or adding it under the `roles` section of your `requirements.yml` file:\n\n```yaml\n---\nroles:\n  - name: elliotweiser.osx-command-line-tools\n\ncollections:\n  - name: geerlingguy.mac\n```\n\n## Usage\n\nHere's an example playbook which installs some Mac Apps (assuming you are signed into the App Store), CLI tools via Homebrew, and Cask Apps using Homebrew:\n\n```yaml\n- hosts: localhost\n  connection: local\n  gather_facts: false\n\n  vars:\n    mas_installed_app_ids:\n      - 424389933 # Final Cut Pro\n      - 497799835 # Xcode\n\n    homebrew_installed_packages:\n      - node\n      - nvm\n      - redis\n      - ssh-copy-id\n      - pv\n\n    homebrew_cask_apps:\n      - docker\n      - firefox\n      - google-chrome\n      - vlc\n\n  roles:\n    - geerlingguy.mac.homebrew\n    - geerlingguy.mac.mas\n```\n\nFor a real-world usage example, see my [Mac Dev Playbook](https://github.com/geerlingguy/mac-dev-playbook).\n\nSee the full documentation for each role in the role's README, linked above.\n\n## License\n\nMIT\n\n## Author\n\nThis collection was created by [Jeff Geerling](https://www.jeffgeerling.com), author of [Ansible for DevOps](https://www.ansiblefordevops.com).\n\n[badge-gh-actions]: https://github.com/geerlingguy/ansible-collection-mac/workflows/CI/badge.svg?event=push\n[link-gh-actions]: https://github.com/geerlingguy/ansible-collection-mac/actions?query=workflow%3ACI\n[badge-collection]: https://img.shields.io/badge/collection-geerlingguy.mac-blue\n[link-galaxy]: https://galaxy.ansible.com/geerlingguy/mac\n[badge-license]: https://img.shields.io/github/license/geerlingguy/ansible-collection-mac.svg\n[link-license]: https://github.com/geerlingguy/ansible-collection-mac/blob/master/LICENSE\n[badge-gh-actions]: https://github.com/geerlingguy/ansible-role-homebrew/workflows/CI/badge.svg?event=push\n"
  },
  {
    "path": "galaxy-deploy.yml",
    "content": "---\n# Automated release playbook for Ansible Collections.\n#\n# Originally based on Ericsysmin's 2020 blog post. Meant to be used in a GitHub\n# Actions CI environment.\n#\n# Requires a ANSIBLE_GALAXY_TOKEN secret to be configured on the GitHub repo.\n#\n# Usage:\n#   ansible-playbook -i 'localhost,' galaxy-deploy.yml \\\n#   -e \"github_tag=${{ github.ref }}\"\n\n- name: Deploy new Collection version to Galaxy.\n  hosts: localhost\n  connection: local\n  gather_facts: false\n\n  vars:\n    namespace: geerlingguy\n    collection: mac\n    # Requires github_tag to be set when calling playbook.\n    release_tag: \"{{ github_tag.split('/')[-1] }}\"\n\n  pre_tasks:\n    - name: Ensure ANSIBLE_GALAXY_TOKEN is set.\n      fail:\n        msg: A valid ANSIBLE_GALAXY_TOKEN must be set.\n      when: \"lookup('env', 'ANSIBLE_GALAXY_TOKEN') | length == 0\"\n\n    - name: Ensure the ~/.ansible directory exists.\n      file:\n        path: ~/.ansible\n        state: directory\n\n    - name: Write the Galaxy token to ~/.ansible/galaxy_token\n      copy:\n        content: |\n          token: {{ lookup('env', 'ANSIBLE_GALAXY_TOKEN') }}\n        dest: ~/.ansible/galaxy_token\n\n  tasks:\n    - name: Ensure the galaxy.yml tag is up to date.\n      lineinfile:\n        path: galaxy.yml\n        regexp: \"^version:\"\n        line: 'version: \"{{ release_tag }}\"'\n\n    - name: Build the collection.\n      command: ansible-galaxy collection build\n      changed_when: true\n\n    - name: Publish the collection.\n      command: >\n        ansible-galaxy collection publish ./{{ namespace }}-{{ collection }}-{{ release_tag }}.tar.gz\n      changed_when: true\n"
  },
  {
    "path": "galaxy.yml",
    "content": "---\nnamespace: geerlingguy\nname: mac\nversion: \"1.2.0\"\nreadme: README.md\n\nauthors:\n  - geerlingguy\n\ndescription: Collection of macOS automation tools for Ansible.\n\n# Either a single license or a list of licenses for content inside of a collection. Ansible Galaxy currently only\n# accepts L(SPDX,https://spdx.org/licenses/) licenses. This key is mutually exclusive with 'license_file'\nlicense:\n  - MIT\n\n# A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character\n# requirements as 'namespace' and 'name'\ntags:\n  - mac\n  - macos\n  - tools\n  - osx\n  - homebrew\n  - brew\n  - workstation\n  - dev\n  - mas\n  - appstore\n  - apps\n  - configure\n  - development\n  - apple\n  - ios\n  - macbook\n  - imac\n  - macmini\n  - server\n\ndependencies:\n  community.general: \">=3.0.0\"\n\nrepository: https://github.com/geerlingguy/ansible-collection-mac\ndocumentation: https://github.com/geerlingguy/ansible-collection-mac\nhomepage: https://www.jeffgeerling.com\nissues: https://github.com/geerlingguy/ansible-collection-mac/issues\n\nbuild_ignore: []\n"
  },
  {
    "path": "meta/runtime.yml",
    "content": "---\nrequires_ansible: \">=2.15.0\"\n"
  },
  {
    "path": "plugins/README.md",
    "content": "# Collections Plugins Directory\n\nThis directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that\nis named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that\nwould contain module utils and modules respectively.\n\nHere is an example directory of the majority of plugins currently supported by Ansible:\n\n```\n└── plugins\n    ├── action\n    ├── become\n    ├── cache\n    ├── callback\n    ├── cliconf\n    ├── connection\n    ├── filter\n    ├── httpapi\n    ├── inventory\n    ├── lookup\n    ├── module_utils\n    ├── modules\n    ├── netconf\n    ├── shell\n    ├── strategy\n    ├── terminal\n    ├── test\n    └── vars\n```\n\nA full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible/2.11/plugins/plugins.html).\n"
  },
  {
    "path": "roles/dock/README.md",
    "content": "# Ansible Role: macOS Dock Automation\n\nThis role automates the use of `dockutil` to manage the items in your macOS Dock. You can add, remove, and arrange Dock items.\n\n## Requirements\n\n  - **Homebrew**: Requires `homebrew` already installed (you can use `geerlingguy.mac.homebrew` to install it on your Mac).\n\n## Role Variables\n\nAvailable variables are listed below, along with example values (see `defaults/main.yml`):\n\n```yaml\ndockitems_remove: []\n```\n\nDock items to remove.\n\n```yaml\ndockitems_persist: []\n```\n\nDock items to add. `pos` parameter is optional and will place the Dock item in a particular position in the Dock.\n\n```yaml\ndockutil_install: true\n```\n\nWhether to install dockutil or not. If set to false you'll need to have installed dockutil prior to the execution of this role. See [this issue](https://github.com/geerlingguy/ansible-collection-mac/issues/42) for alternate installation methods, which may be necessary depending on your version of macOS.\n\n## Dependencies\n\n  - (Soft dependency) `geerlingguy.homebrew`\n\n## Example Playbook\n\n```yaml\n    - hosts: localhost\n\n      vars:\n        dockitems_remove:\n          - Launchpad\n          - TV\n          - Podcasts\n          - 'App Store'\n\n        dockitems_persist:\n          - name: Messages\n            path: \"/Applications/Messages.app/\"\n          - name: Safari\n            path: \"/Applications/Safari.app/\"\n            pos: 2\n          - name: Sublime Text\n            path: \"/Applications/Sublime Text.app/\"\n            pos: 3\n\n      roles:\n        - geerlingguy.mac.homebrew\n        - geerlingguy.mac.dock\n```\n\nSee the [Mac Development Ansible Playbook](https://github.com/geerlingguy/mac-dev-playbook) for an example of this role's usage.\n\n## License\n\nMIT / BSD\n\n## Author Information\n\nThis role was created in 2021 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/).\n\nThe contents of this role were originally created by [@dspolleke](https://github.com/dspolleke) as part of the [`mac-dev-playbook`](https://github.com/geerlingguy/mac-dev-playbook).\n"
  },
  {
    "path": "roles/dock/defaults/main.yml",
    "content": "---\n# Dock items to remove.\ndockitems_remove: []\n# - Launchpad\n# - TV\n# - Podcasts\n# - 'App Store'\n\n# Dock items to add. `pos` parameter is optional and will place the Dock item\n# in a particular position in the Dock.\ndockitems_persist: []\n# - name: \"Sublime Text\"\n#   path: \"/Applications/Sublime Text.app/\"\n#   pos: 5\n\n# Whether to install dockutil or not\ndockutil_install: true\n"
  },
  {
    "path": "roles/dock/tasks/dock-add.yml",
    "content": "---\n- name: \"See if Dock item {{ item.name | default(item) }} exists.\"\n  ansible.builtin.command: \"dockutil --find '{{ item.name }}'\"\n  register: dockitem_exists\n  failed_when: >\n    \"No such file or directory\" in dockitem_exists.stdout\n    or \"command not found\" in dockitem_exists.stdout\n  changed_when: false\n  tags: ['dock']\n\n- name: Get current dock section from output.\n  set_fact:\n    current_section: \"{{ dockitem_exists.stdout | regex_replace('^.*was found in (.*) at slot.*$', '\\\\1') }}\"\n  when: dockitem_exists.rc == 0\n  tags: ['dock']\n\n- name: Ensure Dock item {{ item.name | default(item) }} exists.\n  ansible.builtin.command: |\n    dockutil --add '{{ item.path }}' --label '{{ item.name }}'\n    {% if not ansible_loop.last %}--no-restart{% endif %}\n  when: dockitem_exists.rc > 0 or\n        dockitem_exists.rc == 0 and current_section == 'recent-apps'\n  tags: ['dock']\n\n- name: Pause for 7 seconds between dock changes.\n  ansible.builtin.pause:\n    seconds: 7\n  when:\n    - dockitem_exists.rc > 0 or\n      dockitem_exists.rc == 0 and current_section == 'recent-apps'\n    - ansible_loop.last\n  tags: ['dock']\n"
  },
  {
    "path": "roles/dock/tasks/dock-position.yml",
    "content": "---\n- name: \"Check the current Dock position of {{ item.name | default(item) }}.\"\n  ansible.builtin.command:\n    cmd: dockutil --find '{{ item.name | default(item) }}'\n  register: dock_item_position\n  failed_when: '\"command not found\" in dock_item_position.stdout'\n  changed_when: false\n\n- name: Get current dock item position from output.\n  set_fact:\n    current_position: \"{{ dock_item_position | regex_replace('^.*slot (.*) in.*$', '\\\\1') }}\"\n\n- name: Move dock item to the correct position.\n  ansible.builtin.command:\n    cmd: |\n      dockutil --move '{{ item.name | default(item) }}' --position '{{ item.pos }}'\n      {% if not ansible_loop.last %}--no-restart{% endif %}\n  when: current_position|int != item.pos|int\n"
  },
  {
    "path": "roles/dock/tasks/dock-remove.yml",
    "content": "---\n- name: \"See if {{ item }} is in the Dock.\"\n  ansible.builtin.command:\n    cmd: dockutil --find '{{ item }}'\n  register: dockitem_exists\n  changed_when: false\n  failed_when: >\n    \"No such file or directory\" in dockitem_exists.stdout\n    or \"command not found\" in dockitem_exists.stdout\n  tags: ['dock']\n\n- name: Ensure Dock item {{ item }} is removed.\n  ansible.builtin.command:\n    cmd: |\n      dockutil --remove '{{ item }}'\n      {% if not ansible_loop.last %}--no-restart{% endif %}\n  when: dockitem_exists.rc  == 0\n  tags: ['dock']\n\n- name: Pause for 7 seconds between dock changes.\n  ansible.builtin.pause:\n    seconds: 7\n  when:\n    - dockitem_exists.rc == 0\n    - ansible_loop.last\n  tags: ['dock']\n"
  },
  {
    "path": "roles/dock/tasks/main.yml",
    "content": "---\n- name: Install dockutil.\n  community.general.homebrew:\n    name: dockutil\n    state: present\n  notify:\n    - Clear homebrew cache\n  when: dockutil_install\n  tags: ['dock']\n\n- name: Remove configured Dock items.\n  ansible.builtin.include_tasks: dock-remove.yml\n  loop: \"{{ dockitems_remove }}\"\n  loop_control:\n    extended: true\n    extended_allitems: false\n  tags: ['dock']\n\n- name: Ensure required dock items exist.\n  ansible.builtin.include_tasks: dock-add.yml\n  loop: \"{{ dockitems_persist }}\"\n  loop_control:\n    extended: true\n    extended_allitems: false\n  tags: ['dock']\n\n- name: Ensure dock items are in correct position.\n  ansible.builtin.include_tasks: dock-position.yml\n  when:\n    - item.pos is defined\n    - item.pos > 0\n  loop: \"{{ dockitems_persist }}\"\n  loop_control:\n    extended: true\n    extended_allitems: false\n  tags: ['dock']\n"
  },
  {
    "path": "roles/homebrew/README.md",
    "content": "# Ansible Role: Homebrew\n\nInstalls [Homebrew][homebrew] on MacOS, and configures packages, taps, and cask apps according to supplied variables.\n\n## Requirements\n\nNone.\n\n## Role Variables\n\nAvailable variables are listed below, along with default values (see [`defaults/main.yml`](defaults/main.yml)):\n\n    homebrew_repo: https://github.com/Homebrew/brew\n\nThe GitHub repository for Homebrew core.\n\n    homebrew_prefix: \"{{ (ansible_facts.machine == 'arm64') | ternary('/opt/homebrew', '/usr/local') }}\"\n    homebrew_install_path: \"{{ homebrew_prefix }}{{ '/Homebrew' if ansible_facts.machine != 'arm64' }}\"\n\nThe path where Homebrew will be installed (`homebrew_prefix` is the parent directory). It is recommended you stick to the default, otherwise Homebrew might have some weird issues. If you change this variable, you should also manually create a symlink back to `/opt/homebrew` (or `/usr/local` if you're on an Intel-mac) so things work as Homebrew expects.\n\n    homebrew_brew_bin_path: {{ homebrew_prefix }}/bin\n\nThe path where `brew` will be installed.\n\n    homebrew_installed_packages:\n      - ssh-copy-id\n      - pv\n      - { name: vim, install_options: \"with-luajit,override-system-vi\" }\n\nPackages you would like to make sure are installed via `brew install`. You can optionally add flags to the install by setting an `install_options` property, and if used, you need to explicitly set the `name` for the package as well. By default, no packages are installed (`homebrew_installed_packages: []`).\n\n    homebrew_uninstalled_packages: []\n\nPackages you would like to make sure are _uninstalled_.\n\n    homebrew_upgrade_all_packages: false\n\nWhether to upgrade homebrew and all packages installed by homebrew. If you prefer to manually update packages via `brew` commands, leave this set to `false`.\n\n    homebrew_taps:\n      - { name: my_company/internal_tap, url: 'https://example.com/path/to/tap.git' }\n\nTaps you would like to make sure Homebrew has tapped.\n\n    homebrew_cask_apps:\n      - firefox\n      - { name: virtualbox, install_options:\"debug,appdir=/Applications\" }\n\nApps you would like to have installed via `cask`. [Search][caskroom] for popular apps to see if they're available for install via Cask. Cask will not be used if it is not included in the list of taps in the `homebrew_taps` variable. You can optionally add flags to the install by setting an `install_options` property, and if used, you need to explicitly set the `name` for the package as well. By default, no Cask apps will be installed (`homebrew_cask_apps: []`).\n\n    homebrew_cask_accept_external_apps: true\n\nDefault value is `false` and would result in interruption of further processing of the whole role (and ansible play) in case any app given in `homebrew_cask_apps` is already installed without `cask`. Good for a tightly managed system.\n\nSpecify as `true` instead if you prefer to silently continue if any App is already installed without `cask`. Generally good for a system that is managed with `cask` / `Ansible` as well as other install methods (like manually) at the same time.\n\n    homebrew_cask_uninstalled_apps:\n      - google-chrome\n\nApps you would like to make sure are _uninstalled_.\n\n    homebrew_cask_appdir: /Applications\n\nDirectory where applications installed via `cask` should be installed.\n\n    ansible_become_password: \"\"\n\nSet this to your account password if casks you want installed need elevated privileges while installing (like `microsoft-office`), preferably [encrypted via `ansible-vault`][link-vault-doc].\n\n    homebrew_use_brewfile: true\n\nWhether to install via a Brewfile. If so, you will need to install the `homebrew/bundle` tap, which could be done within `homebrew_taps`.\n\n    homebrew_brewfile_dir: '~'\n\nThe directory where your Brewfile is located.\n\n    homebrew_clear_cache: false\n\nSet to `true` to remove the Hombrew cache after any new software is installed.\n\n    homebrew_user: \"{{ ansible_facts.user_id }}\"\n\nThe user that you would like to install Homebrew as.\n\n    homebrew_group: \"{{ ansible_facts.user_gid }}\"\n\nThe group that you would like to use while installing Homebrew.\n\n    homebrew_folders_additional: []\n\nAny additional folders inside `homebrew_prefix` for which to ensure homebrew user/group ownership.\n\n## Dependencies\n\n  - [elliotweiser.osx-command-line-tools][dep-osx-clt-role]\n\n## Example Playbook\n\n    - hosts: localhost\n      vars:\n        homebrew_installed_packages:\n          - mysql\n      roles:\n        - geerlingguy.mac.homebrew\n\nSee the `tests/local-testing` directory for an example of running this role over\nAnsible's `local` connection. See also:\n[Mac Development Ansible Playbook][mac-dev-playbook].\n\n## License\n\n[MIT][link-license]\n\n## Author Information\n\nThis role was created in 2014 by [Jeff Geerling][author-website], author of\n[Ansible for DevOps][ansible-for-devops].\n\n#### Maintainer(s)\n\n- [Jeff Geerling](https://github.com/geerlingguy)\n- [Elliot Weiser](https://github.com/elliotweiser)\n\n[ansible-for-devops]: https://www.ansiblefordevops.com/\n[author-website]: https://www.jeffgeerling.com/\n[caskroom]: https://caskroom.github.io/search\n[homebrew]: http://brew.sh/\n[dep-osx-clt-role]: https://galaxy.ansible.com/elliotweiser/osx-command-line-tools/\n[link-galaxy]: https://galaxy.ansible.com/geerlingguy/homebrew/\n[link-license]: https://raw.githubusercontent.com/geerlingguy/ansible-role-homebrew/master/LICENSE\n[link-gh-actions]: https://github.com/geerlingguy/ansible-role-homebrew/actions?query=workflow%3ACI\n[mac-dev-playbook]: https://github.com/geerlingguy/mac-dev-playbook\n[link-vault-doc]: https://docs.ansible.com/ansible/latest/user_guide/vault.html#creating-encrypted-variables\n"
  },
  {
    "path": "roles/homebrew/defaults/main.yml",
    "content": "---\nhomebrew_repo: https://github.com/Homebrew/brew\n\nhomebrew_prefix: \"{{ (ansible_facts.machine == 'arm64') | ternary('/opt/homebrew', '/usr/local') }}\"\nhomebrew_install_path: \"{{ homebrew_prefix }}{{ '/Homebrew' if ansible_facts.machine != 'arm64' }}\"\nhomebrew_brew_bin_path: \"{{ homebrew_prefix }}/bin\"\n\nhomebrew_installed_packages: []\n\nhomebrew_uninstalled_packages: []\n\nhomebrew_upgrade_all_packages: false\n\nhomebrew_taps: []\n\nhomebrew_cask_apps: []\n\nhomebrew_cask_uninstalled_apps: []\n\nhomebrew_cask_appdir: /Applications\nhomebrew_cask_accept_external_apps: false\n\n# Set this to your account password if casks need elevated privileges.\n# ansible_become_password: ''\n\nhomebrew_use_brewfile: true\nhomebrew_brewfile_dir: '~'\n\nhomebrew_clear_cache: false\n\nhomebrew_folders_additional: []\n"
  },
  {
    "path": "roles/homebrew/handlers/main.yml",
    "content": "---\n# handlers for ansible-role-homebrew\n\n- name: Clear homebrew cache\n  file:\n    path: \"{{ homebrew_cache_path.stdout | trim }}\"\n    state: absent\n  when: 'homebrew_clear_cache | bool'\n  become: \"{{ (homebrew_user != ansible_facts.user_id) | bool }}\"\n  become_user: \"{{ homebrew_user }}\"\n"
  },
  {
    "path": "roles/homebrew/tasks/main.yml",
    "content": "---\n- name: Determine Homebrew ownership variables\n  set_fact:\n    homebrew_user: '{{ homebrew_user | default(ansible_facts.user_id) }}'\n    homebrew_group: '{{ homebrew_group | default(ansible_facts.user_gid) }}'\n\n# Homebrew setup prerequisites.\n- name: Ensure Homebrew parent directory has correct permissions (Apple Silicon).\n  file:\n    path: \"{{ homebrew_prefix }}\"\n    owner: \"{{ homebrew_user }}\"\n    state: directory\n  become: true\n  when: ansible_facts.machine == 'arm64'\n\n- name: Ensure Homebrew parent directory has correct permissions (Intel).\n  when: ansible_facts.machine == 'x86_64'\n  block:\n    - name: Ensure Homebrew parent directory has correct permissions (MacOS >= 10.13).\n      file:\n        path: \"{{ homebrew_prefix }}\"\n        owner: root\n        state: directory\n      become: true\n      when: \"ansible_facts.distribution_version is version('10.13', '>=')\"\n\n    - name: Ensure Homebrew parent directory has correct permissions (MacOS < 10.13).\n      file:\n        path: \"{{ homebrew_prefix }}\"\n        owner: root\n        group: admin\n        state: directory\n        mode: \"0775\"\n      become: true\n      when: \"ansible_facts.distribution_version is version('10.13', '<')\"\n\n- name: Check if homebrew already exists.\n  stat:\n    path: \"{{ homebrew_brew_bin_path }}/brew\"\n  register: pre_installed_brew\n\n- name: Ensure Homebrew directory exists.\n  file:\n    path: \"{{ homebrew_install_path }}\"\n    owner: \"{{ homebrew_user }}\"\n    group: \"{{ homebrew_group }}\"\n    state: directory\n    mode: \"0775\"\n  become: true\n\n# Clone Homebrew.\n- name: Ensure Homebrew is installed.\n  git:\n    repo: \"{{ homebrew_repo }}\"\n    version: master\n    dest: \"{{ homebrew_install_path }}\"\n    update: false\n    depth: 1\n  become: true\n  become_user: \"{{ homebrew_user }}\"\n  when: not pre_installed_brew.stat.exists\n\n# Adjust Homebrew permissions.\n- name: Ensure proper permissions and ownership on homebrew_brew_bin_path dirs.\n  file:\n    path: \"{{ homebrew_brew_bin_path }}\"\n    state: directory\n    owner: \"{{ homebrew_user }}\"\n    group: \"{{ homebrew_group }}\"\n    mode: \"0775\"\n  become: true\n\n- name: Ensure proper ownership on homebrew_install_path subdirs.\n  file:\n    path: \"{{ homebrew_install_path }}\"\n    state: directory\n    owner: \"{{ homebrew_user }}\"\n    group: \"{{ homebrew_group }}\"\n    recurse: true\n    follow: false\n  become: true\n\n# Place brew binary in proper location and complete setup.\n- name: Check if homebrew binary is already in place.\n  stat:\n    path: \"{{ homebrew_brew_bin_path }}/brew\"\n  register: homebrew_binary\n  check_mode: false\n\n- name: Symlink brew to homebrew_brew_bin_path.\n  file:\n    src: \"{{ homebrew_install_path }}/bin/brew\"\n    dest: \"{{ homebrew_brew_bin_path }}/brew\"\n    state: link\n  when: not homebrew_binary.stat.exists\n  become: true\n\n- name: Add missing folder if not on Apple-chipset\n  set_fact:\n    homebrew_folders_base: \"{{ homebrew_folders_base + ['Homebrew'] }}\"\n  when: ansible_facts.machine != 'arm64'\n\n- name: Ensure proper homebrew folders are in place.\n  file:\n    path: \"{{ homebrew_prefix }}/{{ item }}\"\n    state: directory\n    owner: \"{{ homebrew_user }}\"\n    group: \"{{ homebrew_group }}\"\n  become: true\n  loop: \"{{ homebrew_folders_base + homebrew_folders_additional }}\"\n\n- name: Collect package manager fact.\n  setup:\n    filter: ansible_facts.pkg_mgr\n\n- name: Perform brew installation.\n  # Privilege escalation is only required for inner steps when\n  # the `homebrew_user` doesn't match the `ansible_facts.user_id`\n  become: \"{{ (homebrew_user != ansible_facts.user_id) | bool }}\"\n  become_user: \"{{ homebrew_user }}\"\n  block:\n    - name: Force update brew after installation.\n      command: \"{{ homebrew_brew_bin_path }}/brew update --force\"\n      when: not pre_installed_brew.stat.exists\n\n    - name: Where is the cache?\n      command: \"{{ homebrew_brew_bin_path }}/brew --cache\"\n      register: homebrew_cache_path\n      changed_when: false\n      check_mode: false\n\n    # Tap.\n    - name: Ensure configured taps are tapped.\n      homebrew_tap:\n        tap: '{{ item.name | default(item) }}'\n        url: '{{ item.url | default(omit) }}'\n        state: present\n      loop: \"{{ homebrew_taps }}\"\n\n    # Cask.\n    - name: Ensure blacklisted cask applications are not installed.\n      homebrew_cask:\n        name: \"{{ item }}\"\n        state: absent\n        sudo_password: \"{{ ansible_become_password | default(omit) }}\"\n      loop: \"{{ homebrew_cask_uninstalled_apps }}\"\n\n    - name: Install configured cask applications.\n      homebrew_cask:\n        name: \"{{ item.name | default(item) }}\"\n        state: present\n        install_options: \"{{ item.install_options | default('appdir=' + homebrew_cask_appdir) }}\"\n        accept_external_apps: \"{{ homebrew_cask_accept_external_apps }}\"\n        sudo_password: \"{{ ansible_become_password | default(omit) }}\"\n      loop: \"{{ homebrew_cask_apps }}\"\n      notify:\n        - Clear homebrew cache\n\n    # Brew.\n    - name: Ensure blacklisted homebrew packages are not installed.\n      homebrew:\n        name: \"{{ item }}\"\n        state: absent\n      loop: \"{{ homebrew_uninstalled_packages }}\"\n\n    - name: Ensure configured homebrew packages are installed.\n      homebrew:\n        path: \"{{ homebrew_brew_bin_path }}\"\n        name: \"{{ item.name | default(item) }}\"\n        install_options: \"{{ item.install_options | default(omit) }}\"\n        state: \"{{ item.state | default('present') }}\"\n      loop: \"{{ homebrew_installed_packages }}\"\n      notify:\n        - Clear homebrew cache\n\n    - name: Upgrade all homebrew packages (if configured).\n      homebrew:\n        update_homebrew: true\n        upgrade_all: true\n      when: homebrew_upgrade_all_packages\n      notify:\n        - Clear homebrew cache\n\n    - name: Check for Brewfile.\n      stat:\n        path: \"{{ homebrew_brewfile_dir }}/Brewfile\"\n      register: homebrew_brewfile\n      check_mode: false\n\n    - name: Install from Brewfile.\n      command: \"{{ homebrew_brew_bin_path }}/brew bundle\"\n      args:\n        chdir: \"{{ homebrew_brewfile_dir }}\"\n      when: homebrew_brewfile.stat.exists and homebrew_use_brewfile\n"
  },
  {
    "path": "roles/homebrew/vars/main.yml",
    "content": "---\nhomebrew_folders_base:\n  - Cellar\n  - Frameworks\n  - Caskroom\n  - bin\n  - etc\n  - include\n  - lib\n  - opt\n  - sbin\n  - share\n  - share/zsh\n  - share/zsh/site-functions\n  - var\n"
  },
  {
    "path": "roles/mas/README.md",
    "content": "# Ansible Role: Mac App Store CLI (mas)\n\nInstalls [mas](https://github.com/mas-cli/mas) on macOS, and installs macOS apps from the Mac App Store.\n\n## Requirements\n\n  - **Homebrew**: Requires `homebrew` already installed (you can use `geerlingguy.mac.homebrew` to install it on your Mac).\n  - **Mac App Store account**: You can either sign into the Mac App Store via the GUI before running this role, or you can set the `mas_email` and `mas_password` prior to running the role. For security reasons, if you're going to use this role to sign in, you should use `vars_prompt` for at least the password; don't store unencrypted passwords with your playbooks!\n\n## Role Variables\n\nAvailable variables are listed below, along with default values (see `defaults/main.yml`):\n\n    mas_email: \"\"\n    mas_password: \"\"\n\nThe credentials for your Mac App Store account. The Apps you install should already be purchased by/registered to this account.\n\nIf setting these variables statically (e.g. in an included vars file), you should encrypt the inventory using [Ansible Vault](http://docs.ansible.com/ansible/playbooks_vault.html). Otherwise you can use [`vars_prompt`](http://docs.ansible.com/ansible/playbooks_prompts.html) to prompt for the password at playbook runtime.\n\nIf you leave both blank, and don't prompt for them, the role assumes you've already signed in via other means (e.g. via GUI or `mas signin [email]`), and will not attempt to sign in again.\n\n    mas_signin_dialog: false\n\nFallback to the built-in Mac App Store dialog to complete sign in. If set to yes, you must specify the aforementioned `mas_email` variable which will be autofilled in the dialog and prompt you to enter your password, followed by the 2FA authorization code if enabled on the account.\n\n### Install apps\n\n    mas_installed_apps:\n      - { id: 425264550, name: \"Blackmagic Disk Speed Test (3.0)\" }\n      - { id: 411643860, name: \"DaisyDisk (4.3.2)\" }\n      - { id: 498486288, name: \"Quick Resizer (1.9)\" }\n      - { id: 497799835, name: \"Xcode (8.1)\" }\n\nA list of apps to ensure are installed on the computer using the Mac App Store. You can get IDs for all your existing installed apps with `mas list`, and you can search for IDs with `mas search [App Name]`. The `name` attribute is not authoritative and only used to provide better information in the playbook output.\n\n    mas_upgrade_all_apps: false\n\nWhether to run `mas upgrade`, which will upgrade all installed Mac App Store apps.\n\n    mas_path: \"{{ '/opt/homebrew/bin/mas' if ansible_facts.architecture == 'arm64' else '/usr/local/bin/mas' }}\"\n\nWhen targeting a remote Mac, the PATH isn't always correct leading to the `mas` binary not being found. This variable allows you to override the path to the `mas` binary. The defaults above work for most installations, but you may need to override this if you have a custom Homebrew installation path or other custom setup.\n\n### Remove installed apps\n\n    mas_uninstalled_apps:\n      - { id: 408981434, name: \"iMovie\" }\n      - { id: 409183694, name: \"Keynote\" }\n      - { id: 409201541, name: \"Pages\" }\n      - { id: 409203825, name: \"Numbers\" }\n      - { id: 682658836, name: \"GarageBand\" }\n\nA list of apps to uninstall from the computer, which were installed using the Mac App Store. You can get IDs for all your existing installed apps with `mas list`. The `name` attribute is not authoritative and only used to provide better information in the playbook output.\n\n## Dependencies\n\n  - (Soft dependency) `geerlingguy.homebrew`\n\n## Example Playbook\n\n    - hosts: localhost\n      vars:\n        mas_installed_apps:\n          - { id: 497799835, name: \"Xcode (8.1)\" }\n      roles:\n        - geerlingguy.mac.homebrew\n        - geerlingguy.mac.mas\n\nSee the [Mac Development Ansible Playbook](https://github.com/geerlingguy/mac-dev-playbook) for an example of this role's usage.\n\n## License\n\nMIT / BSD\n\n## Author Information\n\nThis role was created in 2016 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/).\n"
  },
  {
    "path": "roles/mas/defaults/main.yml",
    "content": "---\nmas_email: \"\"\nmas_password: \"\"\nmas_uninstalled_apps: []\nmas_installed_app_ids: [] # Deprecated\nmas_installed_apps:\n  - { id: 425264550, name: \"Blackmagic Disk Speed Test (3.0)\" }\n  - { id: 411643860, name: \"DaisyDisk (4.3.2)\" }\n  - { id: 498486288, name: \"Quick Resizer (1.9)\" }\n  - { id: 497799835, name: \"Xcode (8.1)\" }\nmas_upgrade_all_apps: false\nmas_signin_dialog: false\nmas_path: \"{{ '/opt/homebrew/bin/mas' if ansible_facts.architecture == 'arm64' else '/usr/local/bin/mas' }}\"\n"
  },
  {
    "path": "roles/mas/tasks/main.yml",
    "content": "---\n- name: Ensure MAS is installed.\n  homebrew:\n    name: mas\n    state: present\n\n- name: Get MAS account status\n  command: '\"{{ mas_path }}\" account'\n  register: mas_account_result\n  failed_when: mas_account_result.rc > 1\n  check_mode: false\n  changed_when: false\n  when:\n    - ansible_facts.distribution_version is version('12', '<')\n\n- name: Sign in to MAS when email and password are provided.\n  command: '\"{{ mas_path }}\" signin \"{{ mas_email }}\" \"{{ mas_password }}\"'\n  register: mas_signin_result\n  when:\n    - ansible_facts.distribution_version is version('10.13', '<')\n    - mas_account_result.rc == 1\n    - mas_email is truthy\n    - mas_password is truthy\n    - not mas_signin_dialog\n\n- name: Sign in to MAS when email is provided, and complete password and 2FA using dialog.\n  command: '\"{{ mas_path }}\" signin \"{{ mas_email }}\" \"{{ mas_password }}\" --dialog'\n  register: mas_signin_result\n  when:\n    - ansible_facts.distribution_version is version('10.13', '<')\n    - mas_signin_dialog\n    - mas_account_result.rc == 1\n    - mas_email is truthy\n\n# - name: List installed MAS apps.\n#   command: '\"{{ mas_path }}\" list'\n#   register: mas_list\n#   check_mode: false\n#   changed_when: false\n#   failed_when: mas_list.rc not in [0,1]\n\n- name: Ensure unwanted MAS apps are uninstalled.\n  become: true\n  environment:\n    PATH: \"{{ mas_path | dirname }}:{{ ansible_facts.env.PATH }}\"\n  community.general.mas:\n    id: \"{{ item.id | default(item) }}\"\n    state: absent\n  loop: \"{{ mas_uninstalled_apps }}\"\n  loop_control:\n    label: \"{{ item.name | default(item.id | default(item)) }}\"\n\n- name: Ensure configured MAS apps are installed.\n  become: true\n  environment:\n    PATH: \"{{ mas_path | dirname }}:{{ ansible_facts.env.PATH }}\"\n  community.general.mas:\n    id: \"{{ item.id | default(item) }}\"\n    state: present\n  loop: \"{{ mas_installed_apps + mas_installed_app_ids }}\"\n  loop_control:\n    label: \"{{ item.name | default(item.id | default(item)) }}\"\n\n- name: Upgrade all apps (if configured).\n  become: true\n  environment:\n    PATH: \"{{ mas_path | dirname }}:{{ ansible_facts.env.PATH }}\"\n  community.general.mas:\n    upgrade_all: true\n  when: mas_upgrade_all_apps\n"
  },
  {
    "path": "tests/ansible.cfg",
    "content": "[defaults]\ninventory = inventory\ncollections_path = ../../../\n"
  },
  {
    "path": "tests/inventory",
    "content": "[local]\nlocalhost ansible_connection=local\n"
  },
  {
    "path": "tests/requirements.yml",
    "content": "---\nroles:\n  - name: elliotweiser.osx-command-line-tools\n\ncollections:\n  - name: community.general\n"
  },
  {
    "path": "tests/test.yml",
    "content": "---\n- name: Test the collection.\n  hosts: localhost\n\n  vars:\n    homebrew_clear_cache: true\n\n    homebrew_installed_packages:\n      - ssh-copy-id  # from homebrew/core\n      - nginx-full  # from dengi/nginx\n\n    homebrew_cask_apps:\n      - firefox  # from hombrew/cask\n      - google-chrome  # from hombrew/cask\n\n    homebrew_taps:\n      - name: denji/nginx\n        url: 'https://github.com/denji/homebrew-nginx.git'\n\n    mas_uninstalled_apps: []\n    mas_installed_app_ids: []\n    mas_installed_apps: []\n\n    dockitems_remove:\n      - Launchpad\n      - TV\n\n    dockitems_persist:\n      - name: Safari\n        path: \"/Applications/Safari.app/\"\n        pos: 1\n\n  roles:\n    - elliotweiser.osx-command-line-tools\n    - geerlingguy.mac.homebrew\n    - geerlingguy.mac.mas\n    - geerlingguy.mac.dock\n"
  },
  {
    "path": "tests/uninstall-homebrew.sh",
    "content": "#!/bin/bash\n#\n# Uninstalls Homebrew using the official uninstall script.\n\n# Download and run the uninstall script.\ncurl -sLO https://raw.githubusercontent.com/Homebrew/install/master/uninstall.sh\nchmod +x ./uninstall.sh\nsudo ./uninstall.sh --force\n\n# Clean up Homebrew directories.\nsudo rm -rf /usr/local/Homebrew\nsudo rm -rf /usr/local/Caskroom\nsudo rm -rf /usr/local/bin/brew\nsudo rm -rf /opt/homebrew\n"
  }
]