diff --git a/.travis.yml b/.travis.yml index 385cd6742..fcb2919af 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,9 +2,14 @@ language: python python: "2.7" -before_install: - # Make sure everything's up to date. - - sudo apt-get update -qq +sudo: false + +addons: + apt: + packages: + - php5-cli + - php5-common + - php5-curl install: # Install Ansible. @@ -19,4 +24,4 @@ install: script: # Check the role/playbook's syntax. - "ansible-playbook -i tests/inventory tests/test.yml --syntax-check" - - "ansible-playbook -i tests/inventory tests/test.yml --connection=local --sudo" \ No newline at end of file + - "ansible-playbook -i tests/inventory tests/test.yml --connection=local" diff --git a/CHANGELOG.md b/CHANGELOG.md index d3250a02d..a35632e47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## v2.0 +reworked and more flexible configuration + +* creates release version in a file per release +* more control over shared folders with additional config options +* possibilities for shallow git copy +* optional cache warmup for sf2 +* added tags for specific tasks to allow e.g. cache flush only in later versions +* minified composer.json read overhead +* allow for role hooks + +## v1.0 + +* created stable release for later refactoring + ## v0.3 * working travis-ci tests diff --git a/README.md b/README.md index 89fbadd82..982b000e9 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Ansible role to easily deploy Symfony2 applications. It will clone a git repository, a specific branch or a tag, download and run composer install, and run assetic:dump when finished. -The resulting directory structure is similar to what capifony creates: +The resulting directory structure is similar to what capifony/capistrano creates: ``` project @@ -45,20 +45,24 @@ This playbook is taken from the travis testcase. You can always pass these value - ansible-symfony2 vars: - symfony2_project_root: /test_app - symfony2_project_name: travis-test - symfony2_project_composer_path: /test_app/shared - symfony2_project_repo: https://github.com/symfony/symfony-standard.git - symfony2_project_branch: "2.6" - symfony2_project_php_path: php - symfony2_project_env: prod - symfony2_project_console_opts: '--no-debug' - symfony2_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' - symfony2_project_keep_releases: 5 - symfony2_project_clean_versioning: true + symfony_project_root: /tmp/test_app + symfony_project_name: travis-test + symfony_project_composer_path: /tmp/test_app/shared/composer.phar + symfony_project_repo: https://github.com/symfony/symfony-standard.git + symfony_project_env: prod + + symfony_project_console_opts: '--no-debug' + symfony_project_keep_releases: 5 + + symfony_project_branch: "2.6" + symfony_project_php_path: php + symfony_project_keep_releases: 5 + + symfony_project_manage_composer: True + symfony_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' ``` -Commandline: ```~$ ansible-playbook -i inventory --extra-vars "symfony2_project_release=20150417142505,symfony2_project_branch=master" test.yml``` +Commandline: ```~$ ansible-playbook -i inventory --extra-vars "symfony_project_release=20150417142505,symfony_project_branch=master" test.yml``` ## Role Variables @@ -69,20 +73,35 @@ These are the possible role variables - you only need to have a small set define ```yaml --- - vars: - symfony2_project_root: Path where application will be deployed on server. - symfony2_project_name: Name of project. - symfony2_project_composer_path: path where composer.phar will be stored (e.g. project_root/shared) - symfony2_project_repo: URL of git repository. - symfony2_project_release: Release number, can be numeric, we recommend to set it to release date/time, 20140327100911 - symfony2_project_branch: git branch to deploy. - symfony2_project_php_path: /usr/local/php54/bin/php - symfony2_project_env: prod - symfony2_project_console_opts: '' - symfony2_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' - symfony2_project_keep_releases: 5 - symfony2_project_clean_versioning: true - symfony2_fire_schema_update: false # Runs doctrine:mongodb:schema:update - symfony2_fire_migrations: false # Runs doctrine migrations script + # necessary project vars + symfony_project_root: Path where application will be deployed on server. + symfony_project_composer_path: path where composer.phar will be stored (e.g. project_root/shared) + symfony_project_repo: URL of git repository. + symfony_project_release: Release number, can be numeric, we recommend to set it to release date/time, 20140327100911 + symfony_project_env: prod + + # optional parameters, covered by defaults + symfony_project_post_folder_creation_tasks: task hook after folder creation + symfony_project_pre_cache_warmup_tasks: after cache warmup + symfony_project_pre_live_switch_tasks: before live symlink is switched + symfony_project_post_live_switch_tasks: after live symlink is switched + + symfony_project_branch: git branch, commit hash or version tag to deploy - defaults to master + symfony_project_php_path: php + symfony_project_keep_releases: 5 + symfony_project_git_clone_depth: 1 # uses git shallow copy + symfony_project_console_opts: '' + symfony_project_parameters_file: parameters.yml # optional fixed parameters file in shared + symfony_project_cache_command: cache:warmup + + symfony_project_manage_composer: True + symfony_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' + symfony_project_composer_run_install: True + + symfony_project_enable_cache_warmup: True warmup symfony cache, check out cache command! + symfony_project_fire_schema_update: False # rund mongodb schema update if installed + symfony_project_fire_migrations: run doctrine migrations, if installed + symfony_project_symlink_assets: run assets:create with symlink options ``` ### Role variable defaults @@ -92,16 +111,44 @@ As you can see, the release number default is the current date/time with seconds ```yaml --- - vars - symfony2_project_release: # internally replaced with YmdHis - symfony2_project_branch: master - symfony2_project_php_path: /usr/bin/php - symfony2_project_keep_releases: 5 - symfony2_project_clean_versioning: true - symfony2_project_console_opts: '' - symfony2_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' - symfony2_fire_schema_update: false - symfony2_fire_migrations: false + symfony_project_release: # internally replaced with YmdHis + symfony_project_branch: master + symfony_project_php_path: /usr/bin/php + symfony_project_keep_releases: 5 + symfony_project_git_clone_depth: 1 + symfony_project_console_opts: '' + symfony_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' + symfony_project_fire_migrations: False + symfony_project_symlink_assets: True +``` + +## hooks +If you need any more tasks and stuff in your deployment, you now have the option to include hook scripts. +In my projects there's often e.g. a gulp task that has to be started before finishing the release. You're also free to create more folders yourself or do whatever you need. +As an additional goodie, you can use the internal dynamically created facts from main role: + +``` + symfony_project_release # release timestamp + symfony_current_release # release name + symfony_current_release_dir # fully qualified path to release + symfony_shared_dir # shared folder base path + symfony_console # fully qualified console command path +``` +possible hooks: + +``` + symfony_project_post_folder_creation_tasks: task hook after folder creation + symfony_project_pre_cache_warmup_tasks: after cache warmup + symfony_project_pre_live_switch_tasks: before live symlink is switched + symfony_project_post_live_switch_tasks: after live symlink is switched +``` + +These hooks trigger an include when defined. +Define hooks: +``` + symfony_project_post_folder_creation_tasks: "{{ playbook_dir }}/hooks/post_folder_creation.yml" ``` +The "hooks" dir should be in your deployment project as a subfolder. I'd recommend to use this name as a convention. Also it's convinient to use the name of the hook task as a yml name. ## Dependencies diff --git a/defaults/main.yml b/defaults/main.yml index 32ea330e3..12127df9c 100644 --- a/defaults/main.yml +++ b/defaults/main.yml @@ -1,13 +1,31 @@ --- -# defaults file for symfony2 - -symfony2_project_release: ~ -symfony2_project_branch: master -symfony2_project_php_path: php -symfony2_project_keep_releases: 5 -symfony2_project_clean_versioning: true -symfony2_project_console_opts: '' -symfony2_project_maintain_composer: true -symfony2_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' -symfony2_fire_schema_update: false -symfony2_fire_migrations: false +# necessary project vars + +#symfony_project_root: '' +#symfony_project_composer_path: '' +#symfony_project_repo: '' +#symfony_project_env: '' + +# hooks +symfony_project_post_folder_creation_tasks: ~ +symfony_project_pre_cache_warmup_tasks: ~ +symfony_project_pre_live_switch_tasks: ~ +symfony_project_post_live_switch_tasks: ~ + +symfony_project_release: ~ +symfony_project_branch: master +symfony_project_php_path: php +symfony_project_keep_releases: 5 +symfony_project_git_clone_depth: 1 +symfony_project_console_opts: '' +symfony_project_parameters_file: parameters.yml +symfony_project_cache_command: cache:warmup + +symfony_project_manage_composer: True +symfony_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' +symfony_project_composer_run_install: True + +symfony_project_enable_cache_warmup: True +symfony_project_fire_schema_update: False +symfony_project_fire_migrations: False +symfony_project_symlink_assets: True \ No newline at end of file diff --git a/tasks/assets.yml b/tasks/assets.yml new file mode 100644 index 000000000..983fea2f3 --- /dev/null +++ b/tasks/assets.yml @@ -0,0 +1,8 @@ +--- +- name: Dump assetic assets. + shell: cd {{symfony_current_release_dir}} && {{symfony_project_php_path}} {{symfony_console}} assetic:dump --env={{symfony_project_env}} {{symfony_project_console_opts}} + when: composer_content.stdout.find('assetic-bundle') != -1 + +- name: Symlink/install assets. + shell: cd {{symfony_current_release_dir}} && {{symfony_project_php_path}} {{symfony_console}} assets:install --symlink --env={{symfony_project_env}} {{symfony_project_console_opts}} + when: symfony_project_symlink_assets == True \ No newline at end of file diff --git a/tasks/cache.yml b/tasks/cache.yml new file mode 100644 index 000000000..ceaaeedd2 --- /dev/null +++ b/tasks/cache.yml @@ -0,0 +1,12 @@ +--- +- name: Remove cache dir manually. + file: state=absent path={{symfony_current_release_dir}}/app/cache/{{symfony_project_env}} + when: symfony_project_enable_cache_warmup == True + tags: + - cache + +- name: Warmup sf cache. + shell: cd {{symfony_current_release_dir}} && export SYMFONY_ENV={{symfony_project_env}}; {{symfony_project_php_path}} {{symfony_console}} {{symfony_project_cache_command}} --env={{symfony_project_env}} {{symfony_project_console_opts}} + when: symfony_project_enable_cache_warmup == True + tags: + - cache \ No newline at end of file diff --git a/tasks/clean_releases.yml b/tasks/clean_releases.yml new file mode 100644 index 000000000..8d186bb02 --- /dev/null +++ b/tasks/clean_releases.yml @@ -0,0 +1,4 @@ +--- +- name: Cleanup releases. + shell: "cd {{symfony_project_root}}/releases && ls -t1 | tail -n +$(({{symfony_project_keep_releases}}+1)) | xargs -n1 rm -rf" + when: symfony_project_keep_releases \ No newline at end of file diff --git a/tasks/composer.yml b/tasks/composer.yml new file mode 100644 index 000000000..19deb89c1 --- /dev/null +++ b/tasks/composer.yml @@ -0,0 +1,21 @@ +--- +- name: Check if composer exists. + stat: path={{symfony_project_composer_path}} + register: composer_file + when: symfony_project_manage_composer == True + +- name: Install composer. + get_url: url=https://getcomposer.org/composer.phar dest={{symfony_project_composer_path}} mode=0755 validate_certs=no + when: symfony_project_manage_composer == True and composer_file.stat.exists == False + +- name: Update composer if already exists. + shell: "{{symfony_project_composer_path}} selfupdate" + when: composer_file.stat.exists == True and symfony_project_manage_composer == True + +- name: Link composer to project dir. + file: state=link src={{symfony_project_composer_path}} path={{symfony_current_release_dir}}/composer + when: symfony_project_manage_composer == True + +- name: Run composer install. + shell: cd {{symfony_current_release_dir}} && export SYMFONY_ENV={{symfony_project_env}}; {{symfony_project_php_path}} {{symfony_project_composer_path}} install {{symfony_project_composer_opts}} + when: symfony_project_composer_run_install == True \ No newline at end of file diff --git a/tasks/config.yml b/tasks/config.yml new file mode 100644 index 000000000..dcd916d6b --- /dev/null +++ b/tasks/config.yml @@ -0,0 +1,16 @@ +--- +- name: Check if config dir exists. + stat: path={{symfony_current_release_dir}}/app/config + register: config_dir + +- name: Link configs dir if not yet exists. + file: state=link src={{symfony_shared_dir}}/app/config path={{symfony_current_release_dir}}/app/config + when: config_dir.stat.exists == false + +- name: Check if parameters file exists. + stat: path={{symfony_shared_dir}}/app/config/{{symfony_project_parameters_file}} + register: symfony_parameters + +- name: Create symlink for parameters file from shared directory. + file: state=link src={{symfony_project_root}}/shared/app/config/{{symfony_project_parameters_file}} path={{symfony_current_release_dir}}/app/config/{{symfony_project_parameters_file}} creates={{symfony_current_release_dir}}/app/config/{{symfony_project_parameters_file}} + when: symfony_parameters.stat.exists \ No newline at end of file diff --git a/tasks/empty.yml b/tasks/empty.yml new file mode 100644 index 000000000..73b314ff7 --- /dev/null +++ b/tasks/empty.yml @@ -0,0 +1 @@ +--- \ No newline at end of file diff --git a/tasks/facts.yml b/tasks/facts.yml new file mode 100644 index 000000000..83df7ac35 --- /dev/null +++ b/tasks/facts.yml @@ -0,0 +1,18 @@ +--- +# simplify folder and command vars + +- name: Check/define release name. + set_fact: symfony_project_release={{ lookup('pipe', 'date +%Y%m%d%H%M%S') }} + when: symfony_project_release == None + +- name: Set symfony_current_release. + set_fact: symfony_current_release={{symfony_project_release}}_{{symfony_project_branch}} + +- name: Set symfony_current_release_dir. + set_fact: symfony_current_release_dir={{symfony_project_root}}/releases/{{symfony_current_release}} + +- name: Set symfony_shared_dir. + set_fact: symfony_shared_dir={{symfony_project_root}}/shared + +- name: Set symfony_console. + set_fact: symfony_console={{symfony_current_release_dir}}/app/console \ No newline at end of file diff --git a/tasks/main.yml b/tasks/main.yml index 32c927403..60af02804 100644 --- a/tasks/main.yml +++ b/tasks/main.yml @@ -1,79 +1,56 @@ --- # tasks file for symfony2 -- set_fact: symfony2_project_release={{ lookup('pipe', 'date +%Y%m%d%H%M%S') }} - when: symfony2_project_release == None +- include: facts.yml -- name: Create/prepare directories for release and shared data. +- name: Create/prepare necessary directories for release and shared data. file: state=directory path={{item.path}} with_items: - - { path: "{{symfony2_project_root}}/releases/{{symfony2_project_release}}" } - - { path: "{{symfony2_project_root}}/shared" } - - { path: "{{symfony2_project_root}}/shared/app/config" } - - { path: "{{symfony2_project_root}}/shared/app/logs" } - - { path: "{{symfony2_project_root}}/shared/web/uploads" } + - { path: "{{symfony_current_release_dir}}" } + - { path: "{{symfony_shared_dir}}" } + - { path: "{{symfony_shared_dir}}/app/config" } + - { path: "{{symfony_shared_dir}}/app/logs" } + +#- name: Create/prepare additionaly configured web folders +# file: state=directory path={{item.path}} - name: Pull sources from the repository. - git: repo={{symfony2_project_repo}} dest={{symfony2_project_root}}/releases/{{symfony2_project_release}} version={{symfony2_project_branch}} accept_hostkey=yes + git: repo={{symfony_project_repo}} dest={{symfony_current_release_dir}} version={{symfony_project_branch}} accept_hostkey=yes depth={{symfony_project_git_clone_depth}} + +# read project's composer.json and store output +- shell: cat {{symfony_current_release_dir}}/composer.json + register: composer_content -- name: Clean out versioning. - file: state=absent path={{symfony2_project_root}}/releases/{{symfony2_project_release}}/.git - when: symfony2_project_clean_versioning == true +- include: "{{ symfony_project_post_folder_creation_tasks | default('empty.yml') }}" # will be replaced with symlink - name: Ensure logs directory is not present. - file: state=absent path={{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/logs + file: state=absent path="{{symfony_current_release_dir}}/app/logs" - name: Create symlinks to shared directories. - file: state=link src={{item.src}} path={{item.path}} + file: state=link src="{{symfony_shared_dir}}{{item.src}}" path="{{symfony_current_release_dir}}{{item.path}}" with_items: - - { src: "{{symfony2_project_root}}/shared/app/logs", path: "{{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/logs" } - - { src: "{{symfony2_project_root}}/shared/web/uploads", path: "{{symfony2_project_root}}/releases/{{symfony2_project_release}}/web/uploads" } - -- name: Check if config dir exists. - stat: path={{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/config - register: config_dir - -- name: Link configs dir if not yet exists. - file: state=link src={{symfony2_project_root}}/shared/app/config path={{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/config - when: config_dir.stat.exists == false + - { src: "/app/logs", path: "/app/logs" } -- name: Check if shared/app/config/parameters.ini exists. - stat: path={{symfony2_project_root}}/shared/app/config/parameters.ini - register: parameters_ini +- include: config.yml -- name: Create symlink for app/config/parameters.ini from shared directory. - shell: ln -s {{symfony2_project_root}}/shared/app/config/parameters.ini {{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/config/parameters.ini creates={{symfony2_project_root}}/releases/{{symfony2_project_release}}/app/config/parameters.ini - when: parameters_ini.stat.exists +- include: composer.yml -- name: Check if composer exists. - stat: path={{symfony2_project_composer_path}}/composer.phar - register: composer_file - when: symfony2_project_maintain_composer == true +- include: "{{ symfony_project_pre_cache_warmup_tasks | default('empty.yml') }}" -- name: Install composer. - get_url: url=https://getcomposer.org/composer.phar dest={{symfony2_project_composer_path}} mode=0755 validate_certs=no - when: symfony2_project_maintain_composer == true and composer_file.stat.exists == false +- include: cache.yml -- name: Update composer if already exists. - shell: "{{symfony2_project_composer_path}}/composer.phar selfupdate" - when: symfony2_project_maintain_composer == true and composer_file.stat.exists == true +- include: assets.yml -- name: Run composer install. - shell: cd {{symfony2_project_root}}/releases/{{symfony2_project_release}} && export SYMFONY_ENV={{symfony2_project_env}} && {{symfony2_project_php_path}} {{symfony2_project_composer_path}}/composer.phar install {{symfony2_project_composer_opts}} +- include: migrations.yml -- name: Dump assets. - shell: cd {{symfony2_project_root}}/releases/{{symfony2_project_release}} && {{symfony2_project_php_path}} app/console assetic:dump --env={{symfony2_project_env}} {{symfony2_project_console_opts}} +- name: create release file + copy: content="{{symfony_current_release}}" dest="{{symfony_current_release_dir}}/RELEASE" mode=644 -- name: Run migrations. - shell: cd {{symfony2_project_root}}/releases/{{symfony2_project_release}} && if $(grep doctrine-migrations-bundle composer.json); then {{symfony2_project_php_path}} app/console doctrine:migrations:migrate -n; fi - when: symfony2_fire_migrations == true +- include: "{{ symfony_project_pre_live_switch_tasks | default('empty.yml') }}" -- name: Run mongodb schema update. - shell: cd {{symfony2_project_root}}/releases/{{symfony2_project_release}} && if $(grep mongodb-odm composer.json); then {{symfony2_project_php_path}} app/console doctrine:mongodb:schema:update --no-interaction; fi - when: symfony2_fire_schema_update == true +- name: Create symlink for release. + file: state=link src="{{symfony_current_release_dir}}" path="{{symfony_project_root}}/current" -- name: Create symlink. - file: state=link src={{symfony2_project_root}}/releases/{{symfony2_project_release}} path={{symfony2_project_root}}/current +- include: "{{ symfony_project_post_live_switch_tasks | default('empty.yml') }}" -- name: Cleanup releases. - shell: cd {{symfony2_project_root}}/releases && ls -t1 | tail -n +$(({{symfony2_project_keep_releases}}+1)) | xargs -n1 rm -rf +- include: clean_releases.yml \ No newline at end of file diff --git a/tasks/migrations.yml b/tasks/migrations.yml new file mode 100644 index 000000000..41c6ff6e9 --- /dev/null +++ b/tasks/migrations.yml @@ -0,0 +1,8 @@ +--- +- name: Run migrations. + action: shell {{symfony_project_php_path}} {{symfony_console}} doctrine:migrations:migrate -n + when: symfony_project_fire_migrations == true and composer_content.stdout.find('mongodb-odm') != -1 + +- name: Run mongodb schema update. + action: shell {{symfony_project_php_path}} {{symfony_console}} doctrine:mongodb:schema:update --no-interaction + when: symfony_project_fire_schema_update == true and composer_content.stdout.find('doctrine-migrations-bundle') != -1 \ No newline at end of file diff --git a/tests/test.yml b/tests/test.yml index 158316f7a..8be7e24dd 100644 --- a/tests/test.yml +++ b/tests/test.yml @@ -1,20 +1,33 @@ --- - hosts: localhost - remote_user: root roles: - - geerlingguy.php - ansible-symfony2 vars: - symfony2_project_root: /test_app - symfony2_project_name: travis-test - symfony2_project_composer_path: /test_app/shared - symfony2_project_repo: https://github.com/symfony/symfony-standard.git - symfony2_project_branch: "2.6" - symfony2_project_php_path: php - symfony2_project_env: prod - symfony2_project_console_opts: '--no-debug' - symfony2_project_composer_opts: '--no-dev --optimize-autoloader' - symfony2_project_keep_releases: 5 - symfony2_project_clean_versioning: true - symfony2_fire_migrations: true + # necessary project vars + symfony_project_root: /tmp/test_app + symfony_project_name: travis-test + symfony_project_composer_path: /tmp/test_app/shared/composer.phar + symfony_project_repo: https://github.com/symfony/symfony-standard.git + symfony_project_env: prod + + # optional parameters, covered by defaults + symfony_project_console_opts: '--no-debug' + symfony_project_composer_opts: '--no-dev --optimize-autoloader' + symfony_project_keep_releases: 5 + + symfony_project_release: ~ + symfony_project_branch: "2.6" + symfony_project_php_path: php + symfony_project_keep_releases: 5 + symfony_project_git_clone_depth: 1 + symfony_project_console_opts: '' + + symfony_project_manage_composer: True + symfony_project_composer_opts: '--no-dev --optimize-autoloader --no-interaction' + + symfony_project_parameters_file: parameters.yml + + symfony_project_enable_post_cmd: True + symfony_project_fire_schema_update: True + symfony_project_fire_migrations: True \ No newline at end of file