├── .ansible-lint ├── .gitignore ├── templates ├── composer-project.sh.j2 ├── composer.sh.j2 └── auth.json.j2 ├── .github ├── FUNDING.yml ├── workflows │ ├── release.yml │ ├── ci.yml │ └── stale.yml └── stale.yml ├── molecule └── default │ ├── requirements.yml │ ├── molecule.yml │ └── converge.yml ├── .yamllint ├── tasks ├── project-bin.yml ├── global-require.yml └── main.yml ├── defaults └── main.yml ├── meta └── main.yml ├── LICENSE └── README.md /.ansible-lint: -------------------------------------------------------------------------------- 1 | skip_list: 2 | - 'yaml' 3 | - 'role-name' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | */__pycache__ 3 | *.pyc 4 | .cache 5 | 6 | -------------------------------------------------------------------------------- /templates/composer-project.sh.j2: -------------------------------------------------------------------------------- 1 | export PATH={{ composer_project_path }}:$PATH 2 | -------------------------------------------------------------------------------- /templates/composer.sh.j2: -------------------------------------------------------------------------------- 1 | export PATH=$PATH:{{ composer_home_path }}/vendor/bin 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | --- 3 | github: geerlingguy 4 | patreon: geerlingguy 5 | -------------------------------------------------------------------------------- /templates/auth.json.j2: -------------------------------------------------------------------------------- 1 | { 2 | "github-oauth": { 3 | "github.com": "{{ composer_github_oauth_token }}" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /molecule/default/requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - src: geerlingguy.repo-remi 3 | - src: geerlingguy.git 4 | - src: geerlingguy.php 5 | - src: geerlingguy.php-versions 6 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | 4 | rules: 5 | line-length: 6 | max: 140 7 | level: warning 8 | 9 | ignore: | 10 | .github/workflows/stale.yml 11 | -------------------------------------------------------------------------------- /tasks/project-bin.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add composer_project_path bin directory to global $PATH. 3 | template: 4 | src: composer-project.sh.j2 5 | dest: /etc/profile.d/composer-project.sh 6 | mode: 0644 7 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | role_name_check: 1 3 | dependency: 4 | name: galaxy 5 | options: 6 | ignore-errors: true 7 | driver: 8 | name: docker 9 | platforms: 10 | - name: instance 11 | image: "geerlingguy/docker-${MOLECULE_DISTRO:-rockylinux9}-ansible:latest" 12 | command: ${MOLECULE_DOCKER_COMMAND:-""} 13 | volumes: 14 | - /sys/fs/cgroup:/sys/fs/cgroup:rw 15 | cgroupns_mode: host 16 | privileged: true 17 | pre_build_image: true 18 | provisioner: 19 | name: ansible 20 | playbooks: 21 | converge: ${MOLECULE_PLAYBOOK:-converge.yml} 22 | -------------------------------------------------------------------------------- /tasks/global-require.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Install configured globally-required packages. 3 | command: > 4 | {{ composer_path }} global require {{ item.name }}:{{ item.release | default('@stable') }} --no-progress --no-interaction 5 | creates={{ composer_home_path }}/vendor/{{ item.name }} 6 | environment: 7 | COMPOSER_HOME: "{{ composer_home_path }}" 8 | become: true 9 | become_user: "{{ composer_home_owner }}" 10 | register: composer_global_require_result 11 | with_items: "{{ composer_global_packages }}" 12 | 13 | - name: Add composer_home_path bin directory to global $PATH. 14 | template: 15 | src: composer.sh.j2 16 | dest: /etc/profile.d/composer.sh 17 | mode: 0644 18 | when: composer_add_to_path | bool 19 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | composer_path: /usr/local/bin/composer 3 | composer_keep_updated: false 4 | composer_version: '' 5 | composer_version_branch: '--2' 6 | 7 | # The directory where global packages will be installed. 8 | composer_home_path: '~/.composer' 9 | composer_home_owner: root 10 | composer_home_group: root 11 | 12 | # A list of packages to install globally. See commented examples below for 13 | # usage; the 'release' is optional, and defaults to '@stable'. 14 | composer_global_packages: [] 15 | # - { name: phpunit/phpunit, release: "4.7.x" } 16 | # - { name: phpunit/phpunit, release: "@stable" } 17 | 18 | composer_add_to_path: true 19 | 20 | # Add a project vendor/bin directory to the PATH 21 | composer_add_project_to_path: false 22 | # composer_project_path: /path/to/project/vendor/bin 23 | 24 | # GitHub OAuth token (used to help overcome API rate limits). 25 | composer_github_oauth_token: '' 26 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dependencies: [] 3 | 4 | galaxy_info: 5 | role_name: composer 6 | author: geerlingguy 7 | description: Composer PHP Dependency Manager 8 | company: "Midwestern Mac, LLC" 9 | license: "license (BSD, MIT)" 10 | min_ansible_version: 2.10 11 | platforms: 12 | - name: GenericUNIX 13 | versions: 14 | - all 15 | - name: Fedora 16 | versions: 17 | - all 18 | - name: opensuse 19 | versions: 20 | - all 21 | - name: GenericBSD 22 | versions: 23 | - all 24 | - name: FreeBSD 25 | versions: 26 | - all 27 | - name: Ubuntu 28 | versions: 29 | - all 30 | - name: SLES 31 | versions: 32 | - all 33 | - name: GenericLinux 34 | versions: 35 | - all 36 | - name: Debian 37 | versions: 38 | - all 39 | galaxy_tags: 40 | - packaging 41 | - web 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Jeff Geerling 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /molecule/default/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | #become: true 5 | 6 | vars: 7 | php_enablerepo: "remi,remi-php82" 8 | php_enable_webserver: false 9 | php_version: '8.2' 10 | 11 | # Test a global requirement. 12 | composer_global_packages: 13 | - name: phpunit/phpunit 14 | release: "@stable" 15 | 16 | pre_tasks: 17 | - name: Update apt cache. 18 | apt: update_cache=yes cache_valid_time=600 19 | when: ansible_facts.os_family == 'Debian' 20 | 21 | - name: Set php_packages for Fedora install. 22 | set_fact: 23 | php_packages: 24 | - php 25 | - php-cli 26 | - php-common 27 | - php-devel 28 | - php-mbstring 29 | - php-opcache 30 | - php-pecl-apcu 31 | - php-xml 32 | - php-json 33 | when: ansible_facts.distribution == 'Fedora' 34 | 35 | roles: 36 | - role: geerlingguy.repo-remi 37 | when: ansible_facts.os_family == 'RedHat' and ansible_facts.distribution != 'Fedora' 38 | - role: geerlingguy.git 39 | - role: geerlingguy.php-versions 40 | - role: geerlingguy.php 41 | - role: geerlingguy.composer 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This workflow requires a GALAXY_API_KEY secret present in the GitHub 3 | # repository or organization. 4 | # 5 | # See: https://github.com/marketplace/actions/publish-ansible-role-to-galaxy 6 | # See: https://github.com/ansible/galaxy/issues/46 7 | 8 | name: Release 9 | 'on': 10 | push: 11 | tags: 12 | - '*' 13 | 14 | defaults: 15 | run: 16 | working-directory: 'geerlingguy.composer' 17 | 18 | jobs: 19 | 20 | release: 21 | name: Release 22 | runs-on: ubuntu-latest 23 | steps: 24 | - name: Check out the codebase. 25 | uses: actions/checkout@v4 26 | with: 27 | path: 'geerlingguy.composer' 28 | 29 | - name: Set up Python 3. 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 33 | 34 | - name: Install Ansible. 35 | run: pip3 install ansible-core 36 | 37 | - name: Trigger a new import on Galaxy. 38 | run: >- 39 | ansible-galaxy role import --api-key ${{ secrets.GALAXY_API_KEY }} 40 | $(echo ${{ github.repository }} | cut -d/ -f1) $(echo ${{ github.repository }} | cut -d/ -f2) 41 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 'on': 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | schedule: 9 | - cron: "0 6 * * 0" 10 | 11 | defaults: 12 | run: 13 | working-directory: 'geerlingguy.composer' 14 | 15 | jobs: 16 | 17 | lint: 18 | name: Lint 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Check out the codebase. 22 | uses: actions/checkout@v4 23 | with: 24 | path: 'geerlingguy.composer' 25 | 26 | - name: Set up Python 3. 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 30 | 31 | - name: Install test dependencies. 32 | run: pip3 install yamllint 33 | 34 | - name: Lint code. 35 | run: | 36 | yamllint . 37 | 38 | molecule: 39 | name: Molecule 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | distro: 44 | # See: https://github.com/geerlingguy/docker-rockylinux9-ansible/issues/6 45 | # - rockylinux9 46 | - ubuntu2204 47 | - debian11 48 | 49 | steps: 50 | - name: Check out the codebase. 51 | uses: actions/checkout@v4 52 | with: 53 | path: 'geerlingguy.composer' 54 | 55 | - name: Set up Python 3. 56 | uses: actions/setup-python@v5 57 | with: 58 | python-version: '3.13' # Can't go to 3.14+ until Ansible 13.x 59 | 60 | - name: Install test dependencies. 61 | run: pip3 install ansible molecule molecule-plugins[docker] docker 62 | 63 | - name: Run Molecule tests. 64 | run: molecule test 65 | env: 66 | PY_COLORS: '1' 67 | ANSIBLE_FORCE_COLOR: '1' 68 | MOLECULE_DISTRO: ${{ matrix.distro }} 69 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Close inactive issues 3 | 'on': 4 | schedule: 5 | - cron: "55 9 * * 1" # semi-random time 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | pull-requests: write 13 | steps: 14 | - uses: actions/stale@v8 15 | with: 16 | days-before-stale: 120 17 | days-before-close: 60 18 | exempt-issue-labels: bug,pinned,security,planned 19 | exempt-pr-labels: bug,pinned,security,planned 20 | stale-issue-label: "stale" 21 | stale-pr-label: "stale" 22 | stale-issue-message: | 23 | 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! 24 | 25 | 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. 26 | close-issue-message: | 27 | 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. 28 | stale-pr-message: | 29 | This pr 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! 30 | 31 | 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. 32 | close-pr-message: | 33 | This pr 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. 34 | repo-token: ${{ secrets.GITHUB_TOKEN }} 35 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 30 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - bug 16 | - pinned 17 | - security 18 | - planned 19 | 20 | # Set to true to ignore issues in a project (defaults to false) 21 | exemptProjects: false 22 | 23 | # Set to true to ignore issues in a milestone (defaults to false) 24 | exemptMilestones: false 25 | 26 | # Set to true to ignore issues with an assignee (defaults to false) 27 | exemptAssignees: false 28 | 29 | # Label to use when marking as stale 30 | staleLabel: stale 31 | 32 | # Limit the number of actions per hour, from 1-30. Default is 30 33 | limitPerRun: 30 34 | 35 | pulls: 36 | markComment: |- 37 | 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! 38 | 39 | 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. 40 | 41 | unmarkComment: >- 42 | This pull request is no longer marked for closure. 43 | 44 | closeComment: >- 45 | 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. 46 | 47 | issues: 48 | markComment: |- 49 | 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! 50 | 51 | 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. 52 | 53 | unmarkComment: >- 54 | This issue is no longer marked for closure. 55 | 56 | closeComment: >- 57 | 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. 58 | -------------------------------------------------------------------------------- /tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Check if requested version parameters are valid 3 | fail: 4 | msg: You cannot request a specific version AND keep the composer up to date. 5 | Set either composer_version or composer_keep_updated, but not both. 6 | when: composer_version != '' and composer_keep_updated 7 | 8 | - name: Set php_executable variable to a default if not defined. 9 | set_fact: 10 | php_executable: php 11 | when: php_executable is not defined 12 | 13 | - name: Check if Composer is installed. 14 | stat: "path={{ composer_path }}" 15 | register: composer_bin 16 | 17 | - name: Get Composer installer signature. 18 | uri: 19 | url: https://composer.github.io/installer.sig 20 | return_content: true 21 | check_mode: false 22 | register: composer_installer_signature 23 | when: not composer_bin.stat.exists 24 | 25 | - name: Download Composer installer. 26 | get_url: 27 | url: https://getcomposer.org/installer 28 | dest: /tmp/composer-installer.php 29 | mode: 0755 30 | checksum: "sha384:{{ composer_installer_signature.content }}" 31 | when: not composer_bin.stat.exists 32 | 33 | - name: Run Composer installer. 34 | command: > 35 | {{ php_executable }} composer-installer.php {% if composer_version_branch %} {{ composer_version_branch }}{% elif composer_version %} --version={{ composer_version }}{% endif %} 36 | chdir=/tmp 37 | when: not composer_bin.stat.exists 38 | 39 | - name: Move Composer into globally-accessible location. 40 | command: > 41 | mv /tmp/composer.phar {{ composer_path }} 42 | creates={{ composer_path }} 43 | when: not composer_bin.stat.exists 44 | 45 | - name: Update Composer to latest version (if configured). 46 | command: > 47 | {{ php_executable }} {{ composer_path }} self-update {{ composer_version_branch }} 48 | register: composer_update 49 | changed_when: "'Updating to version' in composer_update.stdout" 50 | when: composer_keep_updated | bool 51 | 52 | - name: Ensure composer directory exists. 53 | become: true 54 | become_user: "{{ composer_home_owner }}" 55 | file: 56 | path: "{{ composer_home_path }}" 57 | owner: "{{ composer_home_owner }}" 58 | group: "{{ composer_home_group }}" 59 | state: directory 60 | mode: 0755 61 | 62 | - name: Add GitHub OAuth token for Composer (if configured). 63 | become: true 64 | become_user: "{{ composer_home_owner }}" 65 | template: 66 | src: "auth.json.j2" 67 | dest: "{{ composer_home_path }}/auth.json" 68 | owner: "{{ composer_home_owner }}" 69 | group: "{{ composer_home_group }}" 70 | mode: 0644 71 | when: composer_github_oauth_token | length > 0 72 | 73 | - include_tasks: global-require.yml 74 | when: composer_global_packages | length > 0 75 | 76 | - include_tasks: project-bin.yml 77 | when: composer_add_project_to_path | bool 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ansible Role: Composer 2 | 3 | [![CI](https://github.com/geerlingguy/ansible-role-composer/actions/workflows/ci.yml/badge.svg)](https://github.com/geerlingguy/ansible-role-composer/actions/workflows/ci.yml) 4 | 5 | Installs Composer, the PHP Dependency Manager, on any Linux or UNIX system. 6 | 7 | ## Requirements 8 | 9 | - `php` (version 5.4+) should be installed and working (you can use the `geerlingguy.php` role to install). 10 | - `git` should be installed and working (you can use the `geerlingguy.git` role to install). 11 | 12 | ## Role Variables 13 | 14 | Available variables are listed below, along with default values (see `defaults/main.yml`): 15 | 16 | composer_path: /usr/local/bin/composer 17 | 18 | The path where composer will be installed and available to your system. Should be in your user's `$PATH` so you can run commands simply with `composer` instead of the full path. 19 | 20 | composer_keep_updated: false 21 | 22 | Set this to `true` to update Composer to the latest release every time the playbook is run. 23 | 24 | composer_home_path: '~/.composer' 25 | composer_home_owner: root 26 | composer_home_group: root 27 | 28 | The `COMPOSER_HOME` path and directory ownership; this is the directory where global packages will be installed. 29 | 30 | composer_version: '' 31 | 32 | You can install a specific release of Composer, e.g. `composer_version: '1.0.0-alpha11'`. If left empty the latest development version will be installed. Note that `composer_keep_updated` will override this variable, as it will always install the latest development version. 33 | 34 | composer_version_branch: '--2' 35 | 36 | You can choose which major branch of composer you wish to use. Default is `--2`. Note that `composer_keep_updated` will update the latest version available for this branch. 37 | 38 | composer_global_packages: [] 39 | 40 | A list of packages to install globally (using `composer global require`). If you want to install any packages globally, add a list item with a dictionary with the `name` of the package and a `release`, e.g. `- { name: phpunit/phpunit, release: "4.7.*" }`. The 'release' is optional, and defaults to `@stable`. 41 | 42 | composer_add_to_path: true 43 | 44 | If `true`, and if there are any configured `composer_global_packages`, the `vendor/bin` directory inside `composer_home_path` will be added to the system's default `$PATH` (for all users). 45 | 46 | composer_project_path: /path/to/project 47 | 48 | Path to a composer project. 49 | 50 | composer_add_project_to_path: false 51 | 52 | If `true`, and if you have configured a `composer_project_path`, the `vendor/bin` directory inside `composer_project_path` will be added to the system's default `$PATH` (for all users). 53 | 54 | composer_github_oauth_token: '' 55 | 56 | GitHub OAuth token, used to avoid GitHub API rate limiting errors when building and rebuilding applications using Composer. Follow GitHub's directions to [Create a personal access token](https://help.github.com/articles/creating-an-access-token-for-command-line-use/) if you run into these rate limit errors. 57 | 58 | php_executable: php 59 | 60 | The executable name or full path to the PHP executable. This is defaulted to `php` if you don't override the variable. 61 | 62 | ### Staying on Composer 1 63 | 64 | While projects are upgrading to support Composer 2, it may be helpful to run Composer 1 instead. To do that, set these variables: 65 | 66 | composer_version_branch: '' 67 | composer_version: '1.10.12' 68 | 69 | ## Dependencies 70 | 71 | None (but make sure you've installed PHP; the `geerlingguy.php` role is recommended). 72 | 73 | ## Example Playbook 74 | 75 | - hosts: servers 76 | roles: 77 | - geerlingguy.composer 78 | 79 | After the playbook runs, `composer` will be placed in `/usr/local/bin/composer` (this location is configurable), and will be accessible via normal system accounts. 80 | 81 | ## License 82 | 83 | MIT / BSD 84 | 85 | ## Author Information 86 | 87 | This role was created in 2014 by [Jeff Geerling](https://www.jeffgeerling.com/), author of [Ansible for DevOps](https://www.ansiblefordevops.com/). 88 | --------------------------------------------------------------------------------