Install and manage Composer Projects using SaltStack

The traditional way to install composer is to download it from composer.org using “curl”, pipe it into PHP and then place the resultant file in your path while also making it executable. Simple enough to script! Then comes the need to create and initialize a project so the vendor directory and required PHP packages are available.

Scripting this into two state files makes the process very easy to implement, apply and manage.

First the install state file, if you followed my guides you know that I am big on creating reusable “roles” that live in /srv/salt/roles/ these are then applied like a shopping list of roles you can reuse for each server/virtual machine (VM) environment.

In this article we will have two roles in the /srv/salt/roles/composer directory. The first is an “install” state file called install.sls and then the project file that lives in your sever state file directory. For a VM called php-test-01 the directory would be /srv/salt/servers/php-test-01/ and the file might be composer-project.sls

Install.sls

Installs into /usr/local/bin but downloads the composer file to /root initially.

get-composer:
  cmd.run:
    - name: 'CURL=`which curl`; $CURL -sS https://getcomposer.org/installer | php'
    - unless: test -f /usr/local/bin/composer
    - cwd: /root/

install-composer:
  cmd.wait:
    - name: mv /root/composer.phar /usr/local/bin/composer
    - cwd: /root/
    - watch:
      - cmd: get-composer

/usr/local/bin:
  composer.installed:
    - no_dev: true
    - require:
      - cmd: install-composer
#
# End of File

composer-project.sls

The next file is the composer-project.sls, this is defined for each project and we can use pillar data to manage the project details, this makes the file below re-usable between servers. For this example, I set /opt as the project but for a web based system, this would normally be something like /var/www/vhosts/<project>/

#
# Composer Project definition
#
# In your pillar define the following (use your own packages).

#composer:
#  project:
#    - name: <proj_name_here>
#  packages:
#    - 'monolog/monolog'
#    - 'friendsofphp/php-cs-fixer'
#    - 'fzaninotto/faker'
#    - 'phpbench/phpbench'
#    - 'phpstan/phpstan'
#    - 'phpstan/phpstan-deprecation-rules'
#    - 'phpstan/phpstan-strict-rules'
#    - 'phpunit/phpunit'

#
# The project packages
#
{% set project_name = salt['pillar.get']("composer:project:name") %}
{% set packages = salt['pillar.get']("composer:packages") %}

# Without composer installed in your PATH
# Note: composer.phar must be executable for state to work properly
/opt/{{project_name}}:
  file.directory:
    - user: root
    - group: root
    - mode: '0775'
    - makedirs: True


{{project_name}}:
  cmd.run:
    - name: composer init
    - cwd: /opt/{{project_name}}/
    - onlyif: test ! -f /opt/{{project_name}}/composer.json

install_composer_packages:
  cmd.run:
    - directory: /opt/{{project_name}}/
    - composer: /usr/local/bin/
    - cwd: /opt/{{project_name}}/
    - name: composer require {% for p in packages %} {{p}} {%endfor%}
#
# End of File
  

Applying the files

In your server’s highstate file simple add two lines:

- roles.composer.install
- servers.php-test-01.composer-project

The output (ignore the error shown, there is a bug notice raised for it):

[root@salt composer]# salt 'php-test-01*' state.sls server.php-test-01.composer-project

php-test-01.your-domain.test:
----------
          ID: /opt/testp
    Function: file.directory
      Result: True
     Comment:
     Started: 15:48:17.105881
    Duration: 6.239 ms
     Changes:
              ----------
              /opt/testp:
                  ----------
                  directory:
                      new
----------
          ID: testp
    Function: cmd.run
        Name: composer init
      Result: True
     Comment: Command "composer init" run
     Started: 15:48:17.131304
    Duration: 980.136 ms
     Changes:
              ----------
              pid:
                  1279758
              retcode:
                  0
              stderr:
                  Writing ./composer.json
              stdout:
----------
          ID: install_composer_packages
    Function: cmd.run
        Name: composer require  php-amqplib/php-amqplib  monolog/monolog  fzaninotto/faker
      Result: True
     Comment: Command "composer require  php-amqplib/php-amqplib  monolog/monolog  fzaninotto/faker" run
     Started: 15:48:18.133930
    Duration: 3519.246 ms
     Changes:
              ----------
              pid:
                  1279771
              retcode:
                  0
              stderr:
                  Composer could not detect the root package (root/testp) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
                  Cannot use fzaninotto/faker's latest version v1.9.2 as it requires php ^5.3.3 || ^7.0 which is not satisfied by your platform.
                  ./composer.json has been updated
                  Composer could not detect the root package (root/testp) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version
                  Running composer update php-amqplib/php-amqplib monolog/monolog fzaninotto/faker
                  Loading composer repositories with package information
                  Updating dependencies
                  Lock file operations: 7 installs, 0 updates, 0 removals
                    - Locking fzaninotto/faker (v1.5.0)
                    - Locking monolog/monolog (3.10.0)
                    - Locking paragonie/constant_time_encoding (v3.1.3)
                    - Locking paragonie/random_compat (v9.99.100)
                    - Locking php-amqplib/php-amqplib (v3.7.4)
                    - Locking phpseclib/phpseclib (3.0.51)
                    - Locking psr/log (3.0.2)
                  Writing lock file
                  Installing dependencies from lock file (including require-dev)
                  Package operations: 7 installs, 0 updates, 0 removals
                    - Downloading fzaninotto/faker (v1.5.0)
                   0/1 [>---------------------------]   0%
                   1/1 [============================] 100%
                    - Installing fzaninotto/faker (v1.5.0): Extracting archive
                    - Installing psr/log (3.0.2): Extracting archive
                    - Installing monolog/monolog (3.10.0): Extracting archive
                    - Installing paragonie/random_compat (v9.99.100): Extracting archive
                    - Installing paragonie/constant_time_encoding (v3.1.3): Extracting archive
                    - Installing phpseclib/phpseclib (3.0.51): Extracting archive
                    - Installing php-amqplib/php-amqplib (v3.7.4): Extracting archive
                   0/7 [>---------------------------]   0%
                   3/7 [============>---------------]  42%
                   7/7 [============================] 100%
                  13 package suggestions were added by new dependencies, use `composer suggest` to see details.
                  Package fzaninotto/faker is abandoned, you should avoid using it. No replacement was suggested.
                  Generating autoload files
                  2 packages you are using are looking for funding.
                  Use the `composer fund` command to find out more!
                  No security vulnerability advisories found.
                  Using version ^3.7 for php-amqplib/php-amqplib
                  Using version ^3.10 for monolog/monolog
                  Using version ^1.5 for fzaninotto/faker
              stdout:

Summary for php-test-01.your-domain.test
------------
Succeeded: 3 (changed=3)
Failed:    0
------------
Total states run:     3
Total run time:   4.506 s
 

Done!

-oOo-

You may also like...

Popular Posts