├── .gitignore ├── README.md ├── ansible.cfg ├── config ├── after_symlink.yml ├── after_symlink_shared.yml ├── before_setup_tasks.yml ├── before_symlink.yml ├── before_symlink_shared.yml └── steps │ ├── chown.yml │ ├── clear_app_cache.yml │ ├── clear_opcache.yml │ ├── composer.yml │ ├── copy_shared.yml │ ├── gulp.yml │ ├── link_uploads.yml │ ├── optimize.yml │ └── restart_queue.yml ├── group_vars ├── .gitignore └── laravel.example.yml ├── hosts.example ├── playbook-deploy.yml ├── playbook-migrate.yml ├── playbook-rollback.yml ├── playbook-seed.yml └── roles ├── carlosbuenosvinos.ansistrano-deploy ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── TESTING.md ├── Vagrantfile ├── ansible.cfg ├── defaults │ └── main.yml ├── docs │ ├── ansistrano-flow.png │ └── figure.md ├── meta │ ├── .galaxy_install_info │ └── main.yml ├── tasks │ ├── anon-stats.yml │ ├── cleanup.yml │ ├── empty.yml │ ├── main.yml │ ├── rsync-deploy.yml │ ├── setup.yml │ ├── symlink-shared.yml │ ├── symlink.yml │ ├── update-code.yml │ └── update-code │ │ ├── copy.yml │ │ ├── copy_unarchive.yml │ │ ├── download.yml │ │ ├── download_unarchive.yml │ │ ├── git.yml │ │ ├── rsync.yml │ │ ├── s3.yml │ │ ├── s3_unarchive.yml │ │ └── unarchive.yml └── test │ ├── main.yml │ ├── my-app │ ├── deploy.yml │ ├── hosts │ ├── index.html │ ├── local-ansistrano │ └── rollback.yml │ ├── roles │ └── local-ansistrano │ ├── tasks │ ├── after-symlink.yml │ ├── create-internal-paths.yml │ └── create-shared-paths.yml │ └── test.yml └── carlosbuenosvinos.ansistrano-rollback ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── defaults └── main.yml ├── meta ├── .galaxy_install_info └── main.yml ├── tasks ├── anon-stats.yml ├── cleanup.yml ├── empty.yml ├── main.yml ├── setup.yml └── symlink.yml └── test ├── roles └── local-ansistrano └── test.yml /.gitignore: -------------------------------------------------------------------------------- 1 | *.retry 2 | *.cache 3 | 4 | production 5 | staging 6 | develop 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Deploy Laravel 5 using Ansible in Capistrano way 2 | 3 | #### Prepare 4 | 5 | Create hosts file for all your environments from example: 6 | 7 | ```bash 8 | cp hosts.example production 9 | ``` 10 | 11 | Create `group_vars/laravel.yml` from example: 12 | 13 | ```bash 14 | cp group_vars/laravel.example.yml group_vars/laravel.yml 15 | ``` 16 | 17 | Edit it to match your environment. 18 | 19 | > Note! Playbook uses [Ansistrano](http://ansistrano.com/), so you are free to update default `group_vars` and use Ansistrano's. 20 | 21 | #### Use 22 | 23 | Playbooks: 24 | 25 | - `playbook-deploy.yml` - Run files deployment 26 | - `playbook-rollback.yml` - Rollback deploy 27 | - `playbook-migrate.yml` - Run migrations 28 | - `playbook-seed.yml` - Run seeding 29 | 30 | ```bash 31 | ansible-playbook playbook-deploy.yml -i production -u root 32 | ``` 33 | 34 | #### Deployment order 35 | 36 | 1. Init releases directory structure 37 | 1. Update repository 38 | 1. Export a copy of the repo to a new release directory 39 | 1. Copy `.env.example` to `shared/.env` if it was absent 40 | 1. Copy `storage` to `shared/storage` if it was absent 41 | 1. Remove local `.env` & `storage` from release directory 42 | 1. Create soft links to shared `.env` & `storage` 43 | 1. Install composer locally 44 | 1. Run `composer self-update` 45 | 1. Run `composer install` 46 | 1. Clear Laravel cache 47 | 1. Optimize code 48 | 1. Run `npm install` 49 | 1. Run `gulp --production` 50 | 1. Change soflink to new release 51 | 1. Update chown 52 | 1. Install cachetool to clear opcache 53 | 1. Run `cachetool opcache:reset` 54 | 1. Restart Laravel queue 55 | 1. Delete old relases 56 | 57 | > Attention! Don't forget to update your `shared/.env` file after first deploy! 58 | -------------------------------------------------------------------------------- /ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | module_lang = en_US.UTF-8 3 | forks = 20 4 | 5 | library = library 6 | 7 | deprecation_warnings=False 8 | 9 | # Temporary directory 10 | remote_tmp = /tmp/.ansible-$(whoami) 11 | 12 | # Cache facts 13 | gathering = smart 14 | ;gathering = explicit 15 | fact_caching = jsonfile 16 | fact_caching_connection = .cache/facts 17 | 18 | # two hours timeout 19 | fact_caching_timeout = 7200 20 | 21 | roles_path = roles 22 | hash_behaviour = merge 23 | host_key_checking = False 24 | timeout = 10 25 | 26 | [privilege_escalation] 27 | # Change user 28 | become = yes 29 | become_user = root 30 | become_method = sudo 31 | 32 | [paramiko_connection] 33 | record_host_keys=False 34 | 35 | [ssh_connection] 36 | control_path = %(directory)s/%%h-%%r 37 | ssh_args = -o "ControlMaster=auto" -o "ControlPersist=600s" 38 | scp_if_ssh = True 39 | sftp_batch_mode = True 40 | # pipelining = True 41 | -------------------------------------------------------------------------------- /config/after_symlink.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: "{{ playbook_dir }}/config/steps/chown.yml" 4 | - include: "{{ playbook_dir }}/config/steps/clear_opcache.yml" 5 | - include: "{{ playbook_dir }}/config/steps/restart_queue.yml" 6 | -------------------------------------------------------------------------------- /config/after_symlink_shared.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: "{{ playbook_dir }}/config/steps/link_uploads.yml" 4 | - include: "{{ playbook_dir }}/config/steps/composer.yml" 5 | -------------------------------------------------------------------------------- /config/before_setup_tasks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - debug: msg="Start deploy from {{ ansistrano_git_repo }} to {{ ansistrano_deploy_to }}" 4 | -------------------------------------------------------------------------------- /config/before_symlink.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - include: "{{ playbook_dir }}/config/steps/clear_app_cache.yml" 4 | - include: "{{ playbook_dir }}/config/steps/optimize.yml" 5 | - include: "{{ playbook_dir }}/config/steps/gulp.yml" 6 | -------------------------------------------------------------------------------- /config/before_symlink_shared.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - stat: path={{ ansistrano_shared_path.stdout }}/.env 4 | register: env_file 5 | 6 | - name: Copy .env.example 7 | copy: 8 | src: "{{ ansistrano_release_path.stdout }}/.env.example" 9 | dest: "{{ ansistrano_shared_path.stdout }}/.env" 10 | remote_src: True 11 | when: env_file.stat.exists == False 12 | 13 | - stat: path={{ ansistrano_shared_path.stdout }}/storage 14 | register: storage_dir 15 | 16 | - name: Copy storage directory 17 | command: "cp -r {{ ansistrano_release_path.stdout }}/storage {{ ansistrano_shared_path.stdout }}/storage" 18 | when: storage_dir.stat.exists == False 19 | -------------------------------------------------------------------------------- /config/steps/chown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Update chown to {{ owner }}:{{ group }} 4 | file: path={{ ansistrano_deploy_to }} state=directory owner={{ owner }} group={{ group }} recurse=yes 5 | -------------------------------------------------------------------------------- /config/steps/clear_app_cache.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Clear application cache 4 | shell: chdir={{ ansistrano_release_path.stdout }} 5 | {{ php_path }} artisan cache:clear 6 | -------------------------------------------------------------------------------- /config/steps/clear_opcache.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check for cachetool.phar 4 | stat: path={{ cachetool_path }} 5 | register: cachetool_file 6 | 7 | - name: Install cachetool 8 | get_url: url=https://gordalina.github.io/cachetool/downloads/cachetool.phar dest={{ cachetool_path }} mode=0755 validate_certs=no force=no 9 | 10 | - name: Run opcache:reset 11 | shell: "{{ php_path }} {{ cachetool_path }} opcache:reset {{ cachetool_options }}" 12 | ignore_errors: True 13 | -------------------------------------------------------------------------------- /config/steps/composer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Check for composer.phar 4 | stat: path={{ composer_path }} 5 | register: composer_file 6 | 7 | - name: Run composer self-update 8 | shell: "{{ composer_path }} selfupdate --no-interaction" 9 | when: composer_file.stat.exists and composer_self_update 10 | register: composer_self_update_result 11 | changed_when: composer_self_update_result.stderr | search('Updating') 12 | 13 | - name: Install composer 14 | get_url: url=https://getcomposer.org/composer.phar dest={{ composer_path }} mode=0755 validate_certs=no force=no 15 | 16 | - name: Run composer install 17 | shell: chdir={{ ansistrano_release_path.stdout }} 18 | {{ php_path }} {{ composer_path }} install {{ composer_options }} 19 | register: composer_install_result 20 | changed_when: composer_install_result.stderr | search('- \w+ing ') 21 | -------------------------------------------------------------------------------- /config/steps/copy_shared.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlanin/ansible-laravel/0dd94d29726e0025b6611fdf7011c4af877b33af/config/steps/copy_shared.yml -------------------------------------------------------------------------------- /config/steps/gulp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Run npm install 4 | shell: chdir={{ ansistrano_release_path.stdout }} 5 | {{ npm_path }} install 6 | when: run_gulp 7 | 8 | - name: Run gulp 9 | shell: chdir={{ ansistrano_release_path.stdout }} 10 | ./node_modules/.bin/gulp --production 11 | when: run_gulp 12 | -------------------------------------------------------------------------------- /config/steps/link_uploads.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Link storage/public to public/storage 4 | file: 5 | state: link 6 | src: "{{ ansistrano_shared_path.stdout }}{{ laravel_uploads_storage }}" 7 | path: "{{ ansistrano_release_path.stdout }}{{ laravel_uploads_public }}" 8 | when: laravel_uploads_storage != false 9 | -------------------------------------------------------------------------------- /config/steps/optimize.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: List artisan commands 4 | shell: chdir={{ ansistrano_release_path.stdout }} 5 | {{ php_path }} artisan 6 | register: artisan_commands 7 | 8 | - name: Optimize code 9 | shell: chdir={{ ansistrano_release_path.stdout }} 10 | {{ php_path }} artisan clear-compiled && {{ php_path }} artisan optimize 11 | when: artisan_commands.stdout.find('optimize') != -1 12 | 13 | - name: Cache routes 14 | shell: chdir={{ ansistrano_release_path.stdout }} 15 | {{ php_path }} artisan route:cache 16 | when: artisan_commands.stdout.find('route:cache') != -1 17 | ignore_errors: yes 18 | -------------------------------------------------------------------------------- /config/steps/restart_queue.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Restart queue 4 | shell: chdir={{ ansistrano_release_path.stdout }} 5 | {{ php_path }} artisan queue:restart 6 | -------------------------------------------------------------------------------- /group_vars/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !laravel.example.yml 4 | -------------------------------------------------------------------------------- /group_vars/laravel.example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Ansistrano defaults 4 | ansistrano_allow_anonymous_stats: no 5 | 6 | # Debug info 7 | ansistrano_before_setup_tasks_file: "{{ playbook_dir }}/config/before_setup_tasks.yml" 8 | # Copy shared files and dirs 9 | ansistrano_before_symlink_shared_tasks_file: "{{ playbook_dir }}/config/before_symlink_shared.yml" 10 | # Link uploads and run composer install 11 | ansistrano_after_symlink_shared_tasks_file: "{{ playbook_dir }}/config/after_symlink_shared.yml" 12 | # Clear application cache and optimize code 13 | ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}/config/before_symlink.yml" 14 | # Chown all and restart queue 15 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/config/after_symlink.yml" 16 | 17 | # Repository info 18 | ansistrano_deploy_via: git 19 | ansistrano_git_repo: git@github.com:foo/bar.git # Update to your git repo 20 | ansistrano_git_branch: master 21 | 22 | # Deployment params 23 | ansistrano_keep_releases: 5 24 | ansistrano_deploy_to: /var/www/laravel # Update to your deploy path 25 | ansistrano_version_dir: releases 26 | ansistrano_current_dir: current 27 | ansistrano_shared_paths: 28 | - .env 29 | - storage 30 | 31 | # Where uploads are stored. Set to false if don't use uploads 32 | laravel_uploads_storage: /storage/app/public 33 | # Where to link them for public 34 | laravel_uploads_public: /public/storage 35 | 36 | owner: www-data 37 | group: www-data 38 | 39 | php_path: /usr/bin/php 40 | 41 | composer_path: "{{ ansistrano_deploy_to }}/composer.phar" 42 | composer_options: '--no-dev --optimize-autoloader --no-interaction' 43 | composer_self_update: true 44 | 45 | cachetool_path: "{{ ansistrano_deploy_to }}/cachetool.phar" 46 | cachetool_options: "--fcgi=/var/run/php/php7.0-fpm.sock" 47 | 48 | run_gulp: true 49 | npm_path: /usr/bin/npm 50 | -------------------------------------------------------------------------------- /hosts.example: -------------------------------------------------------------------------------- 1 | [laravel] 2 | 127.0.0.1 3 | -------------------------------------------------------------------------------- /playbook-deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: laravel 4 | roles: 5 | - carlosbuenosvinos.ansistrano-deploy 6 | -------------------------------------------------------------------------------- /playbook-migrate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: laravel 4 | 5 | tasks: 6 | - name: Migrating DB 7 | shell: chdir={{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }} 8 | {{ php_path }} artisan migrate --force 9 | register: migrate_result 10 | changed_when: "migrate_result.stdout != 'Nothing to migrate.'" 11 | -------------------------------------------------------------------------------- /playbook-rollback.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: laravel 4 | roles: 5 | - carlosbuenosvinos.ansistrano-rollback 6 | -------------------------------------------------------------------------------- /playbook-seed.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - hosts: laravel 4 | 5 | tasks: 6 | - name: Seeding DB 7 | shell: chdir={{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }} 8 | {{ php_path }} artisan db:seed --force 9 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | ### Vagrant template 4 | .vagrant/ 5 | 6 | ### JetBrains template 7 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 8 | 9 | *.iml 10 | 11 | ## Directory-based project format: 12 | .idea/ 13 | # if you remove the above rule, at least ignore the following: 14 | 15 | # User-specific stuff: 16 | # .idea/workspace.xml 17 | # .idea/tasks.xml 18 | # .idea/dictionaries 19 | 20 | # Sensitive or high-churn files: 21 | # .idea/dataSources.ids 22 | # .idea/dataSources.xml 23 | # .idea/sqlDataSources.xml 24 | # .idea/dynamic.xml 25 | # .idea/uiDesigner.xml 26 | 27 | # Gradle: 28 | # .idea/gradle.xml 29 | # .idea/libraries 30 | 31 | # Mongo Explorer plugin: 32 | # .idea/mongoSettings.xml 33 | 34 | ## File-based project format: 35 | *.ipr 36 | *.iws 37 | 38 | ## Plugin-specific files: 39 | 40 | # IntelliJ 41 | /out/ 42 | 43 | # mpeltonen/sbt-idea plugin 44 | .idea_modules/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | 54 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: required 3 | dist: trusty 4 | language: generic 5 | 6 | matrix: 7 | include: 8 | - env: ANSIBLE_VERSION=ppa:ansible/ansible 9 | - env: ANSIBLE_VERSION=ppa:ansible/ansible-1.9 10 | 11 | before_install: 12 | - sudo apt-get -y install software-properties-common 13 | - sudo apt-add-repository -y $ANSIBLE_VERSION 14 | - sudo apt-get -y update 15 | - sudo apt-get -y install ansible 16 | - ansible --version 17 | 18 | script: 19 | - echo localhost > inventory 20 | - ansible-playbook -i inventory --connection=local --sudo -v test/test.yml 21 | 22 | notifications: 23 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 Carlos Buenosvinos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/README.md: -------------------------------------------------------------------------------- 1 | Ansistrano 2 | ========== 3 | 4 | [![Build Status](https://travis-ci.org/ansistrano/deploy.svg?branch=master)](https://travis-ci.org/ansistrano/deploy) 5 | 6 | **ansistrano.deploy** and **ansistrano.rollback** are Ansible roles to easily manage the deployment process for scripting applications such as PHP, Python and Ruby. It's an Ansible port for Capistrano. 7 | 8 | History 9 | ------- 10 | 11 | [Capistrano](http://capistranorb.com/) is a remote server automation tool and it's currently in Version 3. [Version 2.0](https://github.com/capistrano/capistrano/tree/legacy-v2) was originally thought in order to deploy RoR applications. With additional plugins, you were able to deploy non Rails applications such as PHP and Python, with different deployment strategies, stages and much more. I loved Capistrano v2. I have used it a lot. I developed a plugin for it. 12 | 13 | Capistrano 2 was a great tool and it still works really well. However, it is not maintained anymore since the original team is working in v3. This new version does not have the same set of features so it is less powerful and flexible. Besides that, other new tools are becoming easier to use in order to deploy applications, such as Ansible. 14 | 15 | So, I have decided to stop using Capistrano because v2 is not maintained, v3 does not have enough features, and I can do everything Capistrano was doing with Ansible. If you are looking for alternatives, check Fabric or Chef Solo. 16 | 17 | Project name 18 | ------------ 19 | 20 | Ansistrano comes from Ansible + Capistrano, easy, isn't it? 21 | 22 | Early adopters 23 | -------------- 24 | 25 | If you were an early adopter, you should know we have broken BC by moving from using `ansistrano_custom_tasks_path` to individual and specific files per step. See "Role Variables". **The role displays a warning if the variable is defined and although your old playbooks may still run with no errors, you will see that your code is uploaded but custom tasks are not run.** 26 | 27 | Ansistrano anonymous usage stats 28 | -------------------------------- 29 | 30 | We have recently added an extra optional step in Ansistrano so that we can know how many people are deploying their applications with our project. Unfortunately, Ansible Galaxy does not provide any numbers on usage or downloads so this is one of the only ways we have to measure how many users we really have. 31 | 32 | You can check the code we use to store your anonyomus stats at [the ansistrano.com repo](https://github.com/ansistrano/ansistrano.com) and anyway, if you are not comfortable with this, you will always be able to disable this extra step by setting `ansistrano_allow_anonymous_stats` to false in your playbooks. 33 | 34 | Who is using Ansistrano? 35 | ------------------------ 36 | 37 | Is Ansistrano ready to be used? Here are some companies currently using it: 38 | 39 | * [Atrápalo](http://www.atrapalo.com) 40 | * [Another Place Productions](http://www.anotherplaceproductions.com) 41 | * [Suntransfers](http://www.suntransfers.com) 42 | * [Ulabox](https://www.ulabox.com) 43 | * [Euromillions.com](http://euromillions.com/) 44 | * [Uvinum](http://www.uvinum.com) 45 | * [Cycloid](http://www.cycloid.io) 46 | * [Spotahome](https://www.spotahome.com) 47 | * [Ofertix](http://www.ofertix.com) 48 | * [Nice&Crazy](http://www.niceandcrazy.com) 49 | * [Gstock](http://www.g-stock.es) 50 | * [CMP Group](http://www.teamcmp.com) 51 | * [Jolicode](http://jolicode.com/) 52 | * [Wavecontrol](http://monitoring.wavecontrol.com/ca/public/demo/) 53 | 54 | If you are also using it, please let us know via a PR to this document. 55 | 56 | Requirements 57 | ------------ 58 | 59 | In order to deploy your apps with Ansistrano, you will need: 60 | 61 | * Ansible in your deployer machine 62 | 63 | Installation 64 | ------------ 65 | 66 | Ansistrano is an Ansible role distributed globally using [Ansible Galaxy](https://galaxy.ansible.com/). In order to install Ansistrano role you can use the following command. 67 | 68 | ``` 69 | $ ansible-galaxy install carlosbuenosvinos.ansistrano-deploy carlosbuenosvinos.ansistrano-rollback 70 | ``` 71 | 72 | Update 73 | ------ 74 | 75 | If you want to update the role, you need to pass **--force** parameter when installing. Please, check the following command: 76 | 77 | ``` 78 | $ ansible-galaxy install --force carlosbuenosvinos.ansistrano-deploy carlosbuenosvinos.ansistrano-rollback 79 | ``` 80 | 81 | Features 82 | -------- 83 | 84 | * Rollback in seconds (with ansistrano.rollback role) 85 | * Customize your deployment with hooks before and after critical steps 86 | * Save disk space keeping a maximum fixed releases in your hosts 87 | * Choose between SCP (push), RSYNC (push), GIT (pull), Download (HTTP Get) or S3 (get) deployment strategies 88 | 89 | Main workflow 90 | ------------- 91 | 92 | Ansistrano deploys applications following the Capistrano flow. 93 | 94 | * Setup phase: Creates the folder structure to hold your releases 95 | * Code update phase: Puts the new release into your hosts 96 | * Symlink phase: After deploying the new release into your hosts, this step changes the `current` softlink to new the release 97 | * Cleanup phase: Removes any old version based in the `ansistrano_keep_releases` parameter (see "Role Variables") 98 | 99 | ![Ansistrano Flow](https://raw.githubusercontent.com/ansistrano/deploy/master/docs/ansistrano-flow.png) 100 | 101 | Role Variables 102 | -------------- 103 | 104 | ```yaml 105 | - vars: 106 | ansistrano_deploy_from: "{{ playbook_dir }}" # Where my local project is (relative or absolute path) 107 | ansistrano_deploy_to: "/var/www/my-app" # Base path to deploy to. 108 | ansistrano_version_dir: "releases" # Releases folder name 109 | ansistrano_current_dir: "current" # Softlink name. You should rarely changed it. 110 | ansistrano_current_via: "symlink" # Deployment strategy who code should be deployed to current path. Options are symlink or rsync 111 | ansistrano_shared_paths: [] # Shared paths to symlink to release dir 112 | ansistrano_keep_releases: 0 # Releases to keep after a new deployment. See "Pruning old releases". 113 | ansistrano_deploy_via: "rsync" # Method used to deliver the code to the server. Options are copy, rsync, git, s3 or download. 114 | ansistrano_allow_anonymous_stats: yes 115 | 116 | # Variables used in the rsync deployment strategy 117 | ansistrano_rsync_extra_params: "" # Extra parameters to use when deploying with rsync in a single string. Although Ansible allows an array this can cause problems if we try to add multiple --include args as it was reported in https://github.com/ansistrano/deploy/commit/e98942dc969d4e620313f00f003a7ea2eab67e86 118 | ansistrano_rsync_set_remote_user: yes # See [ansible synchronize module](http://docs.ansible.com/ansible/synchronize_module.html). Options are yes, no. 119 | 120 | # Variables used in the Git deployment strategy 121 | ansistrano_git_repo: git@github.com:USERNAME/REPO.git # Location of the git repository 122 | ansistrano_git_branch: master # What version of the repository to check out. This can be the full 40-character SHA-1 hash, the literal string HEAD, a branch name, or a tag name 123 | ansistrano_git_identity_key_path: "" # If specified this file is copied over and used as the identity key for the git commands, path is relative to the playbook in which it is used 124 | 125 | # Variables used in the download deployment strategy 126 | ansistrano_get_url: https://github.com/someproject/somearchive.tar.gz 127 | 128 | # Variables used in the S3 deployment strategy 129 | ansistrano_s3_bucket: s3bucket 130 | ansistrano_s3_object: s3object.tgz # Add the _unarchive suffix to the ansistrano_deploy_via if your object is a package (ie: s3_unarchive) 131 | ansistrano_s3_region: eu-west-1 132 | # Optional variables, omitted by default 133 | ansistrano_s3_aws_access_key: YOUR_AWS_ACCESS_KEY 134 | ansistrano_s3_aws_secret_key: YOUR_AWS_SECRET_KEY 135 | 136 | # Hooks: custom tasks if you need them 137 | ansistrano_before_setup_tasks_file: "{{ playbook_dir }}//my-before-setup-tasks.yml" 138 | ansistrano_after_setup_tasks_file: "{{ playbook_dir }}//my-after-setup-tasks.yml" 139 | ansistrano_before_update_code_tasks_file: "{{ playbook_dir }}//my-before-update-code-tasks.yml" 140 | ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}//my-after-update-code-tasks.yml" 141 | ansistrano_before_symlink_shared_tasks_file: "{{ playbook_dir }}//my-before-symlink-shared-tasks.yml" 142 | ansistrano_after_symlink_shared_tasks_file: "{{ playbook_dir }}//my-after-symlink-shared-tasks.yml" 143 | ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}//my-before-symlink-tasks.yml" 144 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}//my-after-symlink-tasks.yml" 145 | ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}//my-before-cleanup-tasks.yml" 146 | ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}//my-after-cleanup-tasks.yml" 147 | ``` 148 | 149 | `{{ playbook_dir }}` is an Ansible variable that holds the path to the current playbook. 150 | 151 | Deploying 152 | --------- 153 | 154 | In order to deploy with Ansistrano, you need to perform some steps: 155 | 156 | * Create a new `hosts` file. Check [ansible inventory documentation](http://docs.ansible.com/intro_inventory.html) if you need help. This file will identify all the hosts where to deploy to. For multistage environments check [Multistage environments](#multistage-environment-devel-preprod-prod-etc). 157 | * Create a new playbook for deploying your app, for example, `deploy.yml` 158 | * Set up role variables (see [Role Variables](#role-variables)) 159 | * Include the `carlosbuenosvinos.ansistrano-deploy` role as part of a play 160 | * Run the deployment playbook 161 | 162 | ```ansible-playbook -i hosts deploy.yml``` 163 | 164 | If everything has been set up properly, this command will create the following approximate directory structure on your server. Check how the hosts folder structure would look like after one, two and three deployments. 165 | 166 | ``` 167 | -- /var/www/my-app.com 168 | |-- current -> /var/www/my-app.com/releases/20100509145325 169 | |-- releases 170 | | |-- 20100509145325 171 | |-- shared 172 | ``` 173 | 174 | ``` 175 | -- /var/www/my-app.com 176 | |-- current -> /var/www/my-app.com/releases/20100509150741 177 | |-- releases 178 | | |-- 20100509150741 179 | | |-- 20100509145325 180 | |-- shared 181 | ``` 182 | 183 | ``` 184 | -- /var/www/my-app.com 185 | |-- current -> /var/www/my-app.com/releases/20100512131539 186 | |-- releases 187 | | |-- 20100512131539 188 | | |-- 20100509150741 189 | | |-- 20100509145325 190 | |-- shared 191 | ``` 192 | 193 | ### Serial deployments 194 | 195 | To prevent different timestamps when deploying to several servers using the [`serial`](http://docs.ansible.com/playbooks_delegation.html#rolling-update-batch-size) option, you should set the `ansistrano_release_version` variable. 196 | 197 | ```ansible-playbook -i hosts -e "ansistrano_release_version=`date -u +%Y%m%d%H%M%SZ`" deploy.yml``` 198 | 199 | 200 | Rolling back 201 | ----------- 202 | 203 | In order to rollback with Ansistrano, you need to set up the deployment and run the rollback playbook. 204 | 205 | ```ansible-playbook -i hosts rollback.yml``` 206 | 207 | If you try to rollback with zero or one releases deployed, an error will be raised and no actions performed. 208 | 209 | Variables you can tune in rollback role are less than in deploy one: 210 | 211 | ```yaml 212 | - vars: 213 | ansistrano_deploy_to: "/var/www/my-app" # Base path to deploy to. 214 | ansistrano_version_dir: "releases" # Releases folder name 215 | ansistrano_current_dir: "current" # Softlink name. You should rarely changed it. 216 | 217 | # Hooks: custom tasks if you need them 218 | ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}//my-before-symlink-tasks.yml" 219 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}//my-after-symlink-tasks.yml" 220 | ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}//my-before-cleanup-tasks.yml" 221 | ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}//my-after-cleanup-tasks.yml" 222 | ``` 223 | 224 | Multistage environment (devel, preprod, prod, etc.) 225 | --------------------------------------------------- 226 | 227 | If you want to deploy to different environments such as devel, preprod and prod, it's recommended to create different hosts files. When done, you can specify a different host file when running the deployment playbook using the **-i** parameter. On every host file, you can specify different users, password, connection parameters, etc. 228 | 229 | ```ansible-playbook -i hosts_devel deploy.yml``` 230 | 231 | ```ansible-playbook -i hosts_preprod deploy.yml``` 232 | 233 | ```ansible-playbook -i hosts_prod deploy.yml``` 234 | 235 | Hooks: Custom tasks 236 | ------------------- 237 | 238 | You will typically need to reload your webserver after the `Symlink` step, or download your dependencies before `Code update` or even do it in production before the `Symlink`. So, in order to perform your custom tasks you have some hooks that Ansistrano will execute before and after each of the main 3 steps. **This is the main benefit against other similar deployment roles.** 239 | 240 | ``` 241 | -- /my-local-machine/my-app.com 242 | |-- hosts 243 | |-- deploy.yml 244 | |-- my-custom-tasks 245 | | |-- before-code-update.yml 246 | | |-- after-code-update.yml 247 | | |-- before-symlink.yml 248 | | |-- after-symlink.yml 249 | | |-- before-cleanup.yml 250 | | |-- after-cleanup.yml 251 | ``` 252 | 253 | For example, in order to restart apache after `Symlink` step, we'll add in the `after-symlink.yml` 254 | 255 | ``` 256 | - name: Restart Apache 257 | service: name=httpd state=reloaded 258 | ``` 259 | 260 | * **Q: Where would you add sending email notification after a deployment?** 261 | * **Q: (for PHP and Symfony developers) Where would you clean the cache?** 262 | 263 | You can specify a custom tasks file for before and after every step using `ansistrano_before_*_tasks_file` and `ansistrano_after_*_tasks_file` role variables. See "Role Variables" for more information. 264 | 265 | Variables in custom tasks 266 | ------------------------- 267 | 268 | When writing your custom tasks files you may need some variables that Ansistrano makes available to you: 269 | 270 | * ```{{ ansistrano_release_path.stdout }}```: Path to current deployment release (probably the one you are going to use the most) 271 | * ```{{ ansistrano_releases_path.stdout }}```: Path to releases folder 272 | * ```{{ ansistrano_shared_path.stdout }}```: Path to shared folder (where common releases assets can be stored) 273 | * ```{{ ansistrano_release_version }}```: Relative directory name for the release (by default equals to the current timestamp in UTC timezone) 274 | 275 | Pruning old releases 276 | -------------------- 277 | 278 | In continuous delivery environments, you will possibly have a high number of releases in production. Maybe you have tons of space and you don't mind, but it's common practice to keep just a custom number of releases. 279 | 280 | After the deployment, if you want to remove old releases just set the `ansistrano_keep_releases` variable to the total number of releases you want to keep. 281 | 282 | Let's see three deployments with an `ansistrano_keep_releases: 2` configuration: 283 | 284 | ``` 285 | -- /var/www/my-app.com 286 | |-- current -> /var/www/my-app.com/releases/20100509145325 287 | |-- releases 288 | | |-- 20100509145325 289 | |-- shared 290 | ``` 291 | 292 | ``` 293 | -- /var/www/my-app.com 294 | |-- current -> /var/www/my-app.com/releases/20100509150741 295 | |-- releases 296 | | |-- 20100509150741 297 | | |-- 20100509145325 298 | |-- shared 299 | ``` 300 | 301 | ``` 302 | -- /var/www/my-app.com 303 | |-- current -> /var/www/my-app.com/releases/20100512131539 304 | |-- releases 305 | | |-- 20100512131539 306 | | |-- 20100509150741 307 | |-- shared 308 | ``` 309 | 310 | See how the release `20100509145325` has been removed. 311 | 312 | Example Playbook 313 | ---------------- 314 | 315 | In the folder, `example` you can check an example project that shows how to deploy with Ansistrano. In order to run it, you should: 316 | 317 | ``` 318 | $ cd example 319 | $ ansible-playbook -i hosts deploy.yml 320 | ``` 321 | 322 | Sample projects 323 | --------------- 324 | 325 | We have added Ansistrano support for other projects we are working on. 326 | 327 | * LastWishes: Domain-Driven Design PHP Sample App: https://github.com/dddinphp/last-wishes 328 | 329 | As an example, see the execution log of the LastWishes deployment: 330 | 331 | ``` 332 | PLAY [Deploy last wishes app to my server] ************************************ 333 | 334 | GATHERING FACTS *************************************************************** 335 | ok: [quepimquepam.com] 336 | 337 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure deployment base path exists] *** 338 | ok: [quepimquepam.com] 339 | 340 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure releases folder exists] *** 341 | ok: [quepimquepam.com] 342 | 343 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure shared elements folder exists] *** 344 | ok: [quepimquepam.com] 345 | 346 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get release timestamp] *********** 347 | changed: [quepimquepam.com] 348 | 349 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get release path] **************** 350 | changed: [quepimquepam.com] 351 | 352 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get releases path] *************** 353 | changed: [quepimquepam.com] 354 | 355 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get shared path (in rsync case)] *** 356 | changed: [quepimquepam.com] 357 | 358 | TASK: [carlosbuenosvinos.ansistrano-deploy | Rsync application files to remote shared copy (in rsync case)] *** 359 | changed: [quepimquepam.com -> 127.0.0.1] 360 | 361 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy existing code to servers] *** 362 | changed: [quepimquepam.com] 363 | 364 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy existing code to remote servers] *** 365 | skipping: [quepimquepam.com] 366 | 367 | TASK: [carlosbuenosvinos.ansistrano-deploy | Update remote repository] ******** 368 | skipping: [quepimquepam.com] 369 | 370 | TASK: [carlosbuenosvinos.ansistrano-deploy | Export a copy of the repo] ******* 371 | skipping: [quepimquepam.com] 372 | 373 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy code from to servers] ***** 374 | skipping: [quepimquepam.com] 375 | 376 | TASK: [carlosbuenosvinos.ansistrano-deploy | Copy release version into REVISION file] *** 377 | changed: [quepimquepam.com] 378 | 379 | TASK: [carlosbuenosvinos.ansistrano-deploy | Touches up the release code] ***** 380 | changed: [quepimquepam.com] 381 | 382 | TASK: [carlosbuenosvinos.ansistrano-deploy | Change softlink to new release] *** 383 | changed: [quepimquepam.com] 384 | 385 | TASK: [carlosbuenosvinos.ansistrano-deploy | Reload Apache] ******************* 386 | changed: [quepimquepam.com] 387 | 388 | TASK: [carlosbuenosvinos.ansistrano-deploy | Clean up releases] *************** 389 | skipping: [quepimquepam.com] 390 | 391 | PLAY RECAP ******************************************************************** 392 | quepimquepam.com : ok=14 changed=10 unreachable=0 failed=0 393 | ``` 394 | 395 | They're talking about us 396 | ------------------------ 397 | 398 | * [http://alexmoreno.net/ansistrano-deploying-drupal-ansible](http://alexmoreno.net/ansistrano-deploying-drupal-ansible) 399 | * [http://www.ricardclau.com/2015/10/deploying-php-applications-with-ansistrano/](http://www.ricardclau.com/2015/10/deploying-php-applications-with-ansistrano/) 400 | * [http://es.slideshare.net/OrestesCA/ansible-intro-ansible-barcelona-user-group-june-2015](http://es.slideshare.net/OrestesCA/ansible-intro-ansible-barcelona-user-group-june-2015) 401 | * [http://carlosbuenosvinos.com/deploying-symfony-and-php-apps-with-ansistrano/](http://carlosbuenosvinos.com/deploying-symfony-and-php-apps-with-ansistrano/) 402 | * [https://www.youtube.com/watch?v=CPz5zPzzMZE](https://www.youtube.com/watch?v=CPz5zPzzMZE) 403 | * [https://github.com/cbrunnkvist/ansistrano-symfony-deploy](https://github.com/cbrunnkvist/ansistrano-symfony-deploy) 404 | * [https://www.reddit.com/r/ansible/comments/2ezzz5/rapid_rollback_with_ansible/](https://www.reddit.com/r/ansible/comments/2ezzz5/rapid_rollback_with_ansible/) 405 | 406 | License 407 | ------- 408 | 409 | MIT 410 | 411 | Other resources 412 | --------------- 413 | 414 | * [Thoughts on deploying with Ansible](http://www.future500.nl/articles/2014/07/thoughts-on-deploying-with-ansible/) 415 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/TESTING.md: -------------------------------------------------------------------------------- 1 | Testing locally with Ansible 2.x 2 | ================================ 3 | 4 | git clone git@github.com:ansistrano/deploy.git 5 | cd deploy 6 | vagrant up 7 | cd test/my-app 8 | ansible-playbook deploy.yml -i hosts -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant.configure("2") do |config| 2 | config.vm.define 'web01' do |config| 3 | config.vm.box = 'ubuntu/trusty64' 4 | config.vm.hostname = 'web01' 5 | config.vm.synced_folder '.', '/vagrant', disabled: true 6 | end 7 | 8 | config.vm.define 'web02' do |config| 9 | config.vm.box = 'ubuntu/trusty64' 10 | config.vm.hostname = 'web02' 11 | config.vm.synced_folder '.', '/vagrant', disabled: true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | roles_path = ../ -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Path where the code to deploy is 3 | ansistrano_deploy_from: "{{ playbook_dir }}" 4 | 5 | # Path where the code must be deployed to 6 | ansistrano_deploy_to: "/var/www/my-app" 7 | 8 | # Folder name for the releases 9 | ansistrano_version_dir: "releases" 10 | 11 | # Softlink name for the current release 12 | ansistrano_current_dir: "current" 13 | 14 | # Current directory deployment strategy 15 | ansistrano_current_via: "symlink" 16 | 17 | # Shared paths to symlink to release dir 18 | ansistrano_shared_paths: [] 19 | 20 | # Number of releases to keep in your hosts, if 0, unlimited releases will be kept 21 | ansistrano_keep_releases: 0 22 | 23 | # Deployment strategies variables 24 | 25 | # Due to runtime variable evaluation, the ansistrano_deploy_via default is actually 26 | # defined in update-code.yml instead of this file. You can still override it in your 27 | # playbook as needed. 28 | # ansistrano_deploy_via: "rsync" 29 | 30 | ## GIT pull strategy 31 | ansistrano_git_repo: git@github.com:USERNAME/REPO.git 32 | ansistrano_git_branch: master 33 | ansistrano_git_identity_key_path: "" 34 | 35 | ## RSYNC push strategy 36 | ansistrano_rsync_extra_params: "" 37 | ## put user@ for the remote paths. If you have a custom ssh config to define 38 | ## the remote user for a host that does not match the inventory user, 39 | ## you should set this parameter to "no". 40 | ansistrano_rsync_set_remote_user: yes 41 | 42 | ## Download strategy 43 | ansistrano_get_url: https://github.com/someproject/somearchive.tar.gz 44 | 45 | ## S3 get strategy 46 | ansistrano_s3_bucket: s3bucket 47 | ansistrano_s3_object: s3object.tgz 48 | ansistrano_s3_region: eu-west-1 49 | 50 | ## Sends anonymous stats to the www.ansistrano.com servers 51 | ## You can disallow it by just setting this parameter to "no" in your playbook 52 | ansistrano_allow_anonymous_stats: yes -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/docs/ansistrano-flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mlanin/ansible-laravel/0dd94d29726e0025b6611fdf7011c4af877b33af/roles/carlosbuenosvinos.ansistrano-deploy/docs/ansistrano-flow.png -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/docs/figure.md: -------------------------------------------------------------------------------- 1 | http://yuml.me/edit/5473b608 2 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/meta/.galaxy_install_info: -------------------------------------------------------------------------------- 1 | {install_date: 'Thu May 19 09:17:03 2016', version: 1.6.0} 2 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: ansistrano 4 | description: Ansible role to deploy scripting applications like PHP, Python, Ruby, etc. in a Capistrano style 5 | company: Ansistrano 6 | license: MIT 7 | min_ansible_version: 1.8 8 | platforms: 9 | - name: EL 10 | versions: 11 | - all 12 | - name: GenericUNIX 13 | versions: 14 | - all 15 | - name: Fedora 16 | versions: 17 | - all 18 | - name: opensuse 19 | versions: 20 | - all 21 | - name: Amazon 22 | versions: 23 | - all 24 | - name: GenericBSD 25 | versions: 26 | - all 27 | - name: FreeBSD 28 | versions: 29 | - all 30 | - name: Ubuntu 31 | versions: 32 | - all 33 | - name: SLES 34 | versions: 35 | - all 36 | - name: GenericLinux 37 | versions: 38 | - all 39 | - name: Debian 40 | versions: 41 | - all 42 | categories: 43 | - cloud 44 | - web 45 | dependencies: [] 46 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/anon-stats.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Sends anonymous stats if the user is ok with it 3 | - name: ANSISTRANO | Send anonymous stats 4 | uri: 5 | url: http://ansistrano.com/deploy 6 | method: POST 7 | timeout: 5 8 | when: ansistrano_allow_anonymous_stats 9 | run_once: true 10 | ignore_errors: yes 11 | delegate_to: 127.0.0.1 12 | sudo: false 13 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Clean up releases 3 | - name: ANSISTRANO | Clean up releases 4 | shell: ls -1dt {{ ansistrano_releases_path.stdout }}/* | tail -n +{{ ansistrano_keep_releases | int + 1 }} | xargs rm -rf 5 | when: ansistrano_keep_releases > 0 6 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/empty.yml: -------------------------------------------------------------------------------- 1 | # This file is intentionally left empty and it is used in those Capistrano flow steps 2 | # where you don't need to execute any custom tasks -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Check BC for early adopters 3 | fail: msg="ansistrano_custom_tasks_path is not used anymore. Please, check the documentation at https://github.com/ansistrano/deploy" 4 | when: ansistrano_custom_tasks_path is defined 5 | 6 | - include: "{{ ansistrano_before_setup_tasks_file | default('empty.yml') }}" 7 | 8 | - include: setup.yml 9 | 10 | - include: "{{ ansistrano_after_setup_tasks_file | default('empty.yml') }}" 11 | 12 | - include: "{{ ansistrano_before_update_code_tasks_file | default('empty.yml') }}" 13 | 14 | - include: update-code.yml 15 | 16 | - include: "{{ ansistrano_after_update_code_tasks_file | default('empty.yml') }}" 17 | 18 | - include: "{{ ansistrano_before_symlink_shared_tasks_file | default('empty.yml') }}" 19 | 20 | - include: symlink-shared.yml 21 | 22 | - include: "{{ ansistrano_after_symlink_shared_tasks_file | default('empty.yml') }}" 23 | 24 | - include: "{{ ansistrano_before_symlink_tasks_file | default('empty.yml') }}" 25 | 26 | - include: symlink.yml 27 | when: ansistrano_current_via == "symlink" 28 | 29 | - include: rsync-deploy.yml 30 | when: ansistrano_current_via == "rsync" 31 | 32 | - include: "{{ ansistrano_after_symlink_tasks_file | default('empty.yml') }}" 33 | 34 | - include: "{{ ansistrano_before_cleanup_tasks_file | default('empty.yml') }}" 35 | 36 | - include: cleanup.yml 37 | 38 | - include: "{{ ansistrano_after_cleanup_tasks_file | default('empty.yml') }}" 39 | 40 | - include: anon-stats.yml 41 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/rsync-deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Migration check from symlink deployment 4 | - stat: 5 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 6 | register: stat_current_dir 7 | 8 | - name: ANSISTRANO | Remove current folder if it's a symlink 9 | file: 10 | state: absent 11 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 12 | when: stat_current_dir.stat.islnk is defined and stat_current_dir.stat.islnk 13 | 14 | # Perform rsync deployment 15 | - name: ANSISTRANO | Ensure current folder is a directory 16 | file: 17 | state: directory 18 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 19 | 20 | - name: ANSISTRANO | Sync release to new current path 21 | command: rsync -a -F --no-times --delete-after "{{ ansistrano_release_path.stdout }}/" "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}/" 22 | 23 | # Ensure symlinks target paths is absent 24 | - name: ANSISTRANO | Ensure shared paths targets are absent 25 | file: 26 | state: absent 27 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}/{{ item }}" 28 | with_items: "{{ ansistrano_shared_paths }}" 29 | 30 | # Symlinks shared paths 31 | - name: ANSISTRANO | Create softlinks for shared paths 32 | file: 33 | state: link 34 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}/{{ item }}" 35 | src: "{{ item | regex_replace('[^\\/]*', '..') }}/shared/{{ item }}" 36 | with_items: "{{ ansistrano_shared_paths }}" 37 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Setup folders 3 | - name: ANSISTRANO | Ensure deployment base path exists 4 | file: 5 | state: directory 6 | path: "{{ ansistrano_deploy_to }}" 7 | 8 | - name: ANSISTRANO | Set releases path 9 | command: echo "{{ ansistrano_deploy_to }}/{{ ansistrano_version_dir }}" 10 | register: ansistrano_releases_path 11 | 12 | - name: ANSISTRANO | Ensure releases folder exists 13 | file: 14 | state: directory 15 | path: "{{ ansistrano_releases_path.stdout }}" 16 | 17 | - name: ANSISTRANO | Set shared path 18 | command: echo "{{ ansistrano_deploy_to }}/shared" 19 | register: ansistrano_shared_path 20 | 21 | - name: ANSISTRANO | Ensure shared elements folder exists 22 | file: 23 | state: directory 24 | path: "{{ ansistrano_shared_path.stdout }}" 25 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/symlink-shared.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Ensure symlinks target paths is absent 3 | - name: ANSISTRANO | Ensure shared paths targets are absent 4 | file: 5 | state: absent 6 | path: "{{ ansistrano_release_path.stdout }}/{{ item }}" 7 | with_items: "{{ ansistrano_shared_paths }}" 8 | 9 | # Symlinks shared paths 10 | - name: ANSISTRANO | Create softlinks for shared paths 11 | file: 12 | state: link 13 | path: "{{ ansistrano_release_path.stdout }}/{{ item }}" 14 | src: "{{ item | regex_replace('[^\\/]*', '..') }}/../shared/{{ item }}" 15 | with_items: "{{ ansistrano_shared_paths }}" 16 | 17 | # Remove previous .rsync-filter file (rsync current deployment) 18 | - name: ANSISTRANO | Ensure .rsync-filter is absent 19 | file: 20 | state: absent 21 | path: "{{ ansistrano_release_path.stdout }}/.rsync-filter" 22 | when: ansistrano_current_via == "rsync" 23 | 24 | # Setup .rsync-filter file for current rsync deployment (exclude shared folders for rsync current deployment) 25 | - name: ANSISTRANO | Setup .rsync-filter with shared-folders 26 | lineinfile: 27 | dest: "{{ ansistrano_release_path.stdout }}/.rsync-filter" 28 | line: "- /{{ item }}" 29 | create: yes 30 | with_items: "{{ ansistrano_shared_paths }}" 31 | when: ansistrano_current_via == "rsync" 32 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/symlink.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | # Migration check from rsync deployment 4 | - stat: 5 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 6 | register: stat_current_dir 7 | 8 | - name: ANSISTRANO | Remove current folder if it's a directory 9 | file: 10 | state: absent 11 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 12 | when: stat_current_dir.stat.isdir is defined and stat_current_dir.stat.isdir 13 | 14 | # Performs symlink exchange 15 | - name: ANSISTRANO | Change softlink to new release 16 | file: 17 | state: link 18 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 19 | src: "./{{ ansistrano_version_dir }}/{{ ansistrano_release_version }}" 20 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Update code deployment step 3 | - name: ANSISTRANO | Get release version 4 | set_fact: 5 | ansistrano_release_version: "{{ lookup('pipe', 'date -u +%Y%m%d%H%M%SZ') }}" 6 | run_once: true 7 | when: ansistrano_release_version is not defined 8 | delegate_to: 127.0.0.1 9 | 10 | - name: ANSISTRANO | Get release path 11 | command: echo "{{ ansistrano_releases_path.stdout }}/{{ ansistrano_release_version }}" 12 | register: ansistrano_release_path 13 | 14 | - include: "update-code/{{ ansistrano_deploy_via | default('rsync') }}.yml" 15 | 16 | - name: ANSISTRANO | Copy release version into REVISION file 17 | copy: 18 | content: "{{ ansistrano_release_version }}" 19 | dest: "{{ ansistrano_release_path.stdout }}/REVISION" 20 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/copy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | SCP | Create release folder 3 | file: 4 | state: directory 5 | path: "{{ ansistrano_release_path.stdout }}" 6 | 7 | - name: ANSISTRANO | SCP | Deploy existing code to remote servers 8 | copy: 9 | src: "{{ ansistrano_deploy_from }}" 10 | dest: "{{ ansistrano_release_path.stdout }}" 11 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/copy_unarchive.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: copy.yml 3 | 4 | - name: ANSISTRANO | copy_unarchive | Set archived file 5 | set_fact: 6 | ansistrano_archived_file: "{{ ansistrano_release_path.stdout }}/{{ ansistrano_deploy_from | basename }}" 7 | 8 | - include: unarchive.yml 9 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/download.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | download | Create release folder 3 | file: 4 | state: directory 5 | path: "{{ ansistrano_release_path.stdout }}" 6 | 7 | - name: ANSISTRANO | download | Download artifact 8 | get_url: 9 | url: "{{ ansistrano_get_url }}" 10 | dest: "{{ ansistrano_release_path.stdout }}/{{ ansistrano_get_url | basename }}" 11 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/download_unarchive.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: download.yml 3 | 4 | - name: ANSISTRANO | download_unarchive | Set archived file 5 | set_fact: 6 | ansistrano_archived_file: "{{ ansistrano_release_path.stdout }}/{{ ansistrano_get_url | basename }}" 7 | 8 | - include: unarchive.yml 9 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/git.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | GIT | Ensure GIT deployment key is up to date 3 | template: 4 | src: "{{ ansistrano_git_identity_key_path }}" 5 | dest: "{{ ansistrano_deploy_to }}/git_identity_key" 6 | mode: 0400 7 | when: ansistrano_git_identity_key_path|trim != "" 8 | 9 | - name: ANSISTRANO | GIT | Update remote repository 10 | git: 11 | repo: "{{ ansistrano_git_repo }}" 12 | dest: "{{ ansistrano_deploy_to }}/repo" 13 | version: "{{ ansistrano_git_branch }}" 14 | accept_hostkey: true 15 | update: yes 16 | when: ansistrano_git_identity_key_path|trim == '' 17 | 18 | - name: ANSISTRANO | GIT | Update remote repository using SSH key 19 | git: 20 | repo: "{{ ansistrano_git_repo }}" 21 | dest: "{{ ansistrano_deploy_to }}/repo" 22 | version: "{{ ansistrano_git_branch }}" 23 | accept_hostkey: true 24 | update: yes 25 | key_file: "{{ ansistrano_deploy_to }}/git_identity_key" 26 | when: ansistrano_git_identity_key_path|trim != "" 27 | 28 | - name: ANSISTRANO | GIT | Export a copy of the repo 29 | command: git checkout-index -f -a --prefix="{{ ansistrano_release_path.stdout }}/" 30 | args: 31 | chdir: "{{ ansistrano_deploy_to }}/repo" 32 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/rsync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | RSYNC | Get shared path (in rsync case) 3 | command: echo "{{ ansistrano_shared_path.stdout }}/.shared-copy" 4 | register: ansistrano_shared_rsync_copy_path 5 | 6 | - name: ANSISTRANO | RSYNC | Rsync application files to remote shared copy 7 | synchronize: 8 | src: "{{ ansistrano_deploy_from }}" 9 | dest: "{{ ansistrano_shared_rsync_copy_path.stdout }}" 10 | set_remote_user: "{{ ansistrano_rsync_set_remote_user }}" 11 | recursive: yes 12 | delete: yes 13 | archive: yes 14 | compress: yes 15 | rsync_opts: "{{ ansistrano_rsync_extra_params }}" 16 | 17 | - name: ANSISTRANO | RSYNC | Deploy existing code to servers 18 | command: cp -a {{ ansistrano_shared_rsync_copy_path.stdout }} {{ ansistrano_release_path.stdout }} 19 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/s3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | S3 | Create release folder 3 | file: 4 | state: directory 5 | path: "{{ ansistrano_release_path.stdout }}" 6 | 7 | - name: ANSISTRANO | S3 | Get object from S3 8 | s3: 9 | bucket: "{{ ansistrano_s3_bucket }}" 10 | object: "{{ ansistrano_s3_object }}" 11 | dest: "{{ ansistrano_release_path.stdout }}/{{ ansistrano_s3_object }}" 12 | mode: get 13 | region: "{{ ansistrano_s3_region }}" 14 | aws_access_key: "{{ ansistrano_s3_aws_access_key | default(omit) }}" 15 | aws_secret_key: "{{ ansistrano_s3_aws_secret_key | default(omit) }}" 16 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/s3_unarchive.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - include: s3.yml 3 | 4 | - name: ANSISTRANO | s3_unarchive | Set archived file 5 | set_fact: 6 | ansistrano_archived_file: "{{ ansistrano_release_path.stdout }}/{{ ansistrano_s3_object }}" 7 | 8 | - include: unarchive.yml -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/tasks/update-code/unarchive.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Unarchive | Unarchive source 3 | unarchive: 4 | copy: no 5 | src: "{{ ansistrano_archived_file }}" 6 | dest: "{{ ansistrano_release_path.stdout }}" 7 | 8 | - name: ANSISTRANO | Unarchive | Delete archived file 9 | file: 10 | path: "{{ ansistrano_archived_file }}" 11 | state: absent -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Bring up docker containers 3 | hosts: localhost 4 | gather_facts: false 5 | vars: 6 | inventory: 7 | - name: iptables_host_1 8 | image: "chrismeyers/centos6" 9 | roles: 10 | - { role: chrismeyersfsu.provision_docker, provision_docker_company: 'ansible', provision_docker_inventory: "{{ inventory }}" } 11 | 12 | - name: Run ansistrano-deploy Tests 13 | hosts: docker_containers 14 | tasks: 15 | - name: Test ports 16 | command: 'echo "hello world"' -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/my-app/deploy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Deploy example app to my-server.com 3 | hosts: all 4 | vars: 5 | ansistrano_deploy_to: "/tmp/my-app.com" 6 | ansistrano_keep_releases: 2 7 | roles: 8 | - { role: local-ansistrano } 9 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/my-app/hosts: -------------------------------------------------------------------------------- 1 | web01 ansible_user=vagrant ansible_port=2222 ansible_host=127.0.0.1 ansible_ssh_private_key_file=../../.vagrant/machines/web01/virtualbox/private_key 2 | web02 ansible_user=vagrant ansible_port=2200 ansible_host=127.0.0.1 ansible_ssh_private_key_file=../../.vagrant/machines/web02/virtualbox/private_key -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/my-app/index.html: -------------------------------------------------------------------------------- 1 | This is the main file of the project, I know it's not too much, but it will :) 2 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/my-app/local-ansistrano: -------------------------------------------------------------------------------- 1 | ../../ -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/my-app/rollback.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Rollback example app to my-server.com 3 | hosts: all 4 | vars: 5 | ansistrano_deploy_to: "/var/www/my-app.com" 6 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}/../tasks/after-symlink.yml" 7 | roles: 8 | - { role: carlosbuenosvinos.ansistrano-rollback } 9 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/roles/local-ansistrano: -------------------------------------------------------------------------------- 1 | ../../. -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/tasks/after-symlink.yml: -------------------------------------------------------------------------------- 1 | - service: name=httpd state=reloaded 2 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/tasks/create-internal-paths.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # We cannot create the symlinks unless these subfolders exist in the destination release folder 3 | - name: ANSISTRANO | Ensure internal path foo exists 4 | file: 5 | state: directory 6 | path: "{{ ansistrano_release_path.stdout }}/foo" 7 | 8 | - name: ANSISTRANO | Ensure internal path xxx/yyy exists 9 | file: 10 | state: directory 11 | path: "{{ ansistrano_release_path.stdout }}/xxx/yyy" 12 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/tasks/create-shared-paths.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Ensure shared path 1 exists 3 | file: 4 | state: directory 5 | path: "{{ ansistrano_deploy_to }}/shared/blah" 6 | 7 | - name: ANSISTRANO | Ensure shared path 2 exists 8 | file: 9 | state: directory 10 | path: "{{ ansistrano_deploy_to }}/shared/foo/bar" 11 | 12 | - name: ANSISTRANO | Ensure shared path 3 exists 13 | file: 14 | state: directory 15 | path: "{{ ansistrano_deploy_to }}/shared/xxx/yyy/zzz" 16 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-deploy/test/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Given no previous deploy 3 | hosts: all 4 | vars: 5 | ansistrano_deploy_to: "/tmp/my-app.com" 6 | tasks: 7 | - name: Assert ansistrano_deploy_to path does not exist 8 | stat: 9 | path: "{{ ansistrano_deploy_to }}" 10 | register: st 11 | - debug: 12 | msg: "Path does not exist and is a directory" 13 | when: st.stat.exists is defined and not st.stat.exists 14 | 15 | - name: When deploying 16 | hosts: all 17 | vars: 18 | ansistrano_deploy_to: "/tmp/my-app.com" 19 | ansistrano_after_setup_tasks_file: "{{ playbook_dir }}/tasks/create-shared-paths.yml" 20 | ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/tasks/create-internal-paths.yml" 21 | ansistrano_shared_paths: 22 | - blah 23 | - foo/bar 24 | - xxx/yyy/zzz 25 | roles: 26 | - { role: local-ansistrano } 27 | 28 | - name: Then a successful deploy with rsync should be done 29 | hosts: all 30 | vars: 31 | ansistrano_deploy_to: "/tmp/my-app.com" 32 | tasks: 33 | - name: Assert ansistrano_deploy_to path does exist 34 | stat: 35 | path: "{{ ansistrano_deploy_to }}" 36 | register: st 37 | - debug: 38 | msg: "Path exists and is a directory" 39 | when: st.stat.exists is defined and st.stat.exists 40 | - name: Assert ansistrano_deploy_to/current path does exist 41 | stat: 42 | path: "{{ ansistrano_deploy_to }}/current" 43 | register: st 44 | - fail: 45 | msg: "Path is not a symlink" 46 | when: st.stat.exists is defined and st.stat.exists and st.stat.islnk == False 47 | - debug: 48 | msg: "Path exists and is a symlink" 49 | when: st.stat.exists is defined and st.stat.exists and st.stat.islnk 50 | 51 | - name: When deploying with rsync current strategy 52 | hosts: all 53 | vars: 54 | ansistrano_deploy_to: "/tmp/my-app.com" 55 | ansistrano_after_setup_tasks_file: "{{ playbook_dir }}/tasks/create-shared-paths.yml" 56 | ansistrano_after_update_code_tasks_file: "{{ playbook_dir }}/tasks/create-internal-paths.yml" 57 | ansistrano_current_via: "rsync" 58 | ansistrano_shared_paths: 59 | - blah 60 | - foo/bar 61 | - xxx/yyy/zzz 62 | roles: 63 | - { role: local-ansistrano } 64 | 65 | - name: Then a successful deploy with rsync should be done 66 | hosts: all 67 | vars: 68 | ansistrano_deploy_to: "/tmp/my-app.com" 69 | tasks: 70 | - name: Assert ansistrano_deploy_to path does exist 71 | stat: 72 | path: "{{ ansistrano_deploy_to }}" 73 | register: st 74 | - debug: 75 | msg: "Path exists and is a directory" 76 | when: st.stat.exists is defined and st.stat.exists 77 | - name: Assert ansistrano_deploy_to/current path does exist 78 | stat: 79 | path: "{{ ansistrano_deploy_to }}/current" 80 | register: st 81 | - fail: 82 | msg: "Path is not a directory" 83 | when: st.stat.exists is defined and st.stat.exists and st.stat.isdir == False 84 | - debug: 85 | msg: "Path exists and is a directory" 86 | when: st.stat.exists is defined and st.stat.exists and st.stat.isdir 87 | 88 | - name: Given no previous deploy with Git 89 | hosts: all 90 | vars: 91 | ansistrano_deploy_to: "/tmp/git/my-app.com" 92 | tasks: 93 | - name: Assert ansistrano_deploy_to path does not exist 94 | stat: 95 | path: "{{ ansistrano_deploy_to }}" 96 | register: st 97 | - debug: 98 | msg: "Path does not exist and is a directory" 99 | when: st.stat.exists is defined and not st.stat.exists 100 | 101 | - name: When deploying using Git 102 | hosts: all 103 | vars: 104 | ansistrano_deploy_via: "git" 105 | ansistrano_git_repo: https://github.com/ansistrano/deploy.git 106 | ansistrano_git_branch: master 107 | ansistrano_deploy_to: "/tmp/git/my-app.com" 108 | roles: 109 | - { role: local-ansistrano } 110 | 111 | - name: Then a successful deploy with git should be done 112 | hosts: all 113 | vars: 114 | ansistrano_deploy_to: "/tmp/git/my-app.com" 115 | tasks: 116 | - name: Assert ansistrano_deploy_to path does exist 117 | stat: 118 | path: "{{ ansistrano_deploy_to }}" 119 | register: st 120 | - debug: 121 | msg: "Path exists and is a directory" 122 | when: st.stat.exists is defined and st.stat.exists 123 | 124 | - name: Given no previous deploy with download strategy 125 | hosts: all 126 | vars: 127 | ansistrano_deploy_to: "/tmp/download/my-app.com" 128 | tasks: 129 | - name: Assert ansistrano_deploy_to path does not exist 130 | stat: 131 | path: "{{ ansistrano_deploy_to }}" 132 | register: st 133 | - debug: 134 | msg: "Path does not exist and is a directory" 135 | when: st.stat.exists is defined and not st.stat.exists 136 | 137 | - name: When deploying using download strategy 138 | hosts: all 139 | vars: 140 | ansistrano_deploy_via: "download_unarchive" 141 | ansistrano_get_url: https://github.com/ansistrano/deploy/archive/1.4.1.zip 142 | ansistrano_deploy_to: "/tmp/download/my-app.com" 143 | roles: 144 | - { role: local-ansistrano } 145 | 146 | - name: Then a successful deploy with download strategy should be done 147 | hosts: all 148 | vars: 149 | ansistrano_deploy_to: "/tmp/download/my-app.com" 150 | tasks: 151 | - name: Assert ansistrano_deploy_to path does exist 152 | stat: 153 | path: "{{ ansistrano_deploy_to }}" 154 | register: st 155 | - debug: 156 | msg: "Path exists and is a directory" 157 | when: st.stat.exists is defined and st.stat.exists 158 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | 3 | ### Vagrant template 4 | .vagrant/ 5 | 6 | ### JetBrains template 7 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 8 | 9 | *.iml 10 | 11 | ## Directory-based project format: 12 | .idea/ 13 | # if you remove the above rule, at least ignore the following: 14 | 15 | # User-specific stuff: 16 | # .idea/workspace.xml 17 | # .idea/tasks.xml 18 | # .idea/dictionaries 19 | 20 | # Sensitive or high-churn files: 21 | # .idea/dataSources.ids 22 | # .idea/dataSources.xml 23 | # .idea/sqlDataSources.xml 24 | # .idea/dynamic.xml 25 | # .idea/uiDesigner.xml 26 | 27 | # Gradle: 28 | # .idea/gradle.xml 29 | # .idea/libraries 30 | 31 | # Mongo Explorer plugin: 32 | # .idea/mongoSettings.xml 33 | 34 | ## File-based project format: 35 | *.ipr 36 | *.iws 37 | 38 | ## Plugin-specific files: 39 | 40 | # IntelliJ 41 | /out/ 42 | 43 | # mpeltonen/sbt-idea plugin 44 | .idea_modules/ 45 | 46 | # JIRA plugin 47 | atlassian-ide-plugin.xml 48 | 49 | # Crashlytics plugin (for Android Studio and IntelliJ) 50 | com_crashlytics_export_strings.xml 51 | crashlytics.properties 52 | crashlytics-build.properties 53 | 54 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | sudo: required 3 | dist: trusty 4 | language: generic 5 | 6 | matrix: 7 | include: 8 | - env: ANSIBLE_VERSION=ppa:ansible/ansible 9 | - env: ANSIBLE_VERSION=ppa:ansible/ansible-1.9 10 | 11 | before_install: 12 | - sudo apt-get -y install software-properties-common 13 | - sudo apt-add-repository -y $ANSIBLE_VERSION 14 | - sudo apt-get -y update 15 | - sudo apt-get -y install ansible 16 | - ansible --version 17 | 18 | script: 19 | - echo localhost > inventory 20 | - ansible-playbook -i inventory --connection=local --sudo -v test/test.yml -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Carlos Buenosvinos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/README.md: -------------------------------------------------------------------------------- 1 | Ansistrano 2 | ========== 3 | 4 | **ansistrano.deploy** and **ansistrano.rollback** are Ansible roles to easily manage the deployment process for scripting applications such as PHP, Python and Ruby. It's an Ansible port for Capistrano. 5 | 6 | History 7 | ------- 8 | 9 | [Capistrano](http://capistranorb.com/) is a remote server automation tool and it's currently in Version 3. [Version 2.0](https://github.com/capistrano/capistrano/tree/legacy-v2) was originally thought in order to deploy RoR applications. With additional plugins, you were able to deploy non Rails applications such as PHP and Python, with different deployment strategies, stages and much more. I loved Capistrano v2. I have used it a lot. I developed a plugin for it. 10 | 11 | Capistrano 2 was a great tool and it still works really well. However, it is not maintained anymore since the original team is working in v3. This new version does not have the same set of features so it is less powerful and flexible. Besides that, other new tools are becoming easier to use in order to deploy applications, such as Ansible. 12 | 13 | So, I have decided to stop using Capistrano because v2 is not maintained, v3 does not have enough features, and I can do everything Capistrano was doing with Ansible. If you are looking for alternatives, check Fabric or Chef Solo. 14 | 15 | Project name 16 | ------------ 17 | 18 | Ansistrano comes from Ansible + Capistrano, easy, isn't it? 19 | 20 | Early adopters 21 | -------------- 22 | 23 | If you were an early adopter, you should know we have broken BC by moving from using `ansistrano_custom_tasks_path` to individual and specific files per step. See "Role Variables". **The role displays a warning if the variable is defined and although your old playbooks may still run with no errors, you will see that your code is uploaded but custom tasks are not run.** 24 | 25 | Ansistrano anonymous usage stats 26 | -------------------------------- 27 | 28 | We have recently added an extra optional step in Ansistrano so that we can know how many people are deploying their applications with our project. Unfortunately, Ansible Galaxy does not provide any numbers on usage or downloads so this is one of the only ways we have to measure how many users we really have. 29 | 30 | You can check the code we use to store your anonyomus stats at [the ansistrano.com repo](https://github.com/ansistrano/ansistrano.com) and anyway, if you are not comfortable with this, you will always be able to disable this extra step by setting `ansistrano_allow_anonymous_stats` to false in your playbooks. 31 | 32 | Who is using Ansistrano? 33 | ------------------------ 34 | 35 | Is Ansistrano ready to be used? Here are some companies currently using it: 36 | 37 | * [Atrápalo](http://www.atrapalo.com) 38 | * [Another Place Productions](http://www.anotherplaceproductions.com) 39 | * [Suntransfers](http://www.suntransfers.com) 40 | * [Ulabox](https://www.ulabox.com) 41 | * [Euromillions.com](http://euromillions.com/) 42 | * [Uvinum](http://www.uvinum.com) 43 | * [Cycloid](http://www.cycloid.io) 44 | * [Spotahome](https://www.spotahome.com) 45 | * [Ofertix](http://www.ofertix.com) 46 | * [Nice&Crazy](http://www.niceandcrazy.com) 47 | * [Gstock](http://www.g-stock.es) 48 | * [CMP Group](http://www.teamcmp.com) 49 | 50 | If you are also using it, please let us know via a PR to this document. 51 | 52 | Requirements 53 | ------------ 54 | 55 | In order to deploy your apps with Ansistrano, you will need: 56 | 57 | * Ansible in your deployer machine 58 | 59 | Installation 60 | ------------ 61 | 62 | Ansistrano is an Ansible role distributed globally using [Ansible Galaxy](https://galaxy.ansible.com/). In order to install Ansistrano role you can use the following command. 63 | 64 | ``` 65 | $ ansible-galaxy install carlosbuenosvinos.ansistrano-deploy carlosbuenosvinos.ansistrano-rollback 66 | ``` 67 | 68 | Update 69 | ------ 70 | 71 | If you want to update the role, you need to pass **--force** parameter when installing. Please, check the following command: 72 | 73 | ``` 74 | $ ansible-galaxy install --force carlosbuenosvinos.ansistrano-deploy carlosbuenosvinos.ansistrano-rollback 75 | ``` 76 | 77 | Features 78 | -------- 79 | 80 | * Rollback in seconds (with ansistrano.rollback role) 81 | * Customize your deployment with hooks before and after critical steps 82 | * Save disk space keeping a maximum fixed releases in your hosts 83 | * Choose between SCP (push), RSYNC (push) or GIT (pull) deployment strategies 84 | 85 | Main workflow 86 | ------------- 87 | 88 | Ansistrano deploys applications following the Capistrano flow. 89 | 90 | * Setup phase: Creates the folder structure to hold your releases 91 | * Code update phase: Puts the new release into your hosts 92 | * Symlink phase: After deploying the new release into your hosts, this step changes the `current` softlink to new the release 93 | * Cleanup phase: Removes any old version based in the `ansistrano_keep_releases` parameter (see "Role Variables") 94 | 95 | ![Ansistrano Flow](https://raw.githubusercontent.com/ansistrano/deploy/master/docs/ansistrano-flow.png) 96 | 97 | Role Variables 98 | -------------- 99 | 100 | ```yaml 101 | - vars: 102 | ansistrano_deploy_to: "/var/www/my-app" # Base path to deploy to. 103 | ansistrano_version_dir: "releases" # Releases folder name 104 | ansistrano_current_dir: "current" # Softlink name. You should rarely changed it. 105 | ansistrano_remove_rolled_back: yes # You can change this setting in order to keep the rolled back release in the server for later inspection 106 | ansistrano_allow_anonymous_stats: yes 107 | 108 | # Hooks: custom tasks if you need them 109 | ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}//my-before-symlink-tasks.yml" 110 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}//my-after-symlink-tasks.yml" 111 | ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}//my-before-cleanup-tasks.yml" 112 | ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}//my-after-cleanup-tasks.yml" 113 | ``` 114 | 115 | `{{ playbook_dir }}` is an Ansible variable that holds the path to the current playbook. 116 | 117 | Deploying 118 | --------- 119 | 120 | In order to deploy with Ansistrano, you need to perform some steps: 121 | 122 | * Create a new `hosts` file. Check [ansible inventory documentation](http://docs.ansible.com/intro_inventory.html) if you need help. This file will identify all the hosts where to deploy to. For multistage environments check "Multistage environments" 123 | * Create a new playbook for deploying your app, for example, deploy.yml 124 | * Include carlosbuenosvinos.ansible-deploy role 125 | * Set up role variables (see "Role Variables") 126 | * Run the deployment playbook 127 | 128 | ```ansible-playbook -i hosts deploy.yml``` 129 | 130 | If everything has been set up properly, this command will create the following approximate directory structure on your server. Check how the hosts folder structure would look like after one, two and three deployments. 131 | 132 | ``` 133 | -- /var/www/my-app.com 134 | |-- current -> /var/www/my-app.com/releases/20100509145325 135 | |-- releases 136 | | |-- 20100509145325 137 | |-- shared 138 | ``` 139 | 140 | ``` 141 | -- /var/www/my-app.com 142 | |-- current -> /var/www/my-app.com/releases/20100509150741 143 | |-- releases 144 | | |-- 20100509150741 145 | | |-- 20100509145325 146 | |-- shared 147 | ``` 148 | 149 | ``` 150 | -- /var/www/my-app.com 151 | |-- current -> /var/www/my-app.com/releases/20100512131539 152 | |-- releases 153 | | |-- 20100512131539 154 | | |-- 20100509150741 155 | | |-- 20100509145325 156 | |-- shared 157 | ``` 158 | 159 | Rollbacking 160 | ----------- 161 | 162 | In order to rollback with Ansistrano, you need to set up the deployment and run the rollback playbook. 163 | 164 | ```ansible-playbook -i hosts rollback.yml``` 165 | 166 | If you try to roll back with zero or one releases deployed, an error will be raised and no actions performed. 167 | 168 | Variables you can tune in rollback role are less than in deploy one: 169 | 170 | ```yaml 171 | - vars: 172 | ansistrano_deploy_to: "/var/www/my-app" # Base path to deploy to. 173 | ansistrano_version_dir: "releases" # Releases folder name 174 | ansistrano_current_dir: "current" # Softlink name. You should rarely changed it. 175 | 176 | # Hooks: custom tasks if you need them 177 | ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}//my-before-symlink-tasks.yml" 178 | ansistrano_after_symlink_tasks_file: "{{ playbook_dir }}//my-after-symlink-tasks.yml" 179 | ansistrano_before_cleanup_tasks_file: "{{ playbook_dir }}//my-before-cleanup-tasks.yml" 180 | ansistrano_after_cleanup_tasks_file: "{{ playbook_dir }}//my-after-cleanup-tasks.yml" 181 | ``` 182 | 183 | Multistage environment (devel, preprod, prod, etc.) 184 | --------------------------------------------------- 185 | 186 | If you want to deploy to different environments such as devel, preprod and prod, it's recommended to create different hosts files. When done, you can specify a different host file when running the deployment playbook using the **-i** parameter. On every host file, you can specify different users, password, connection parameters, etc. 187 | 188 | ```ansible-playbook -i hosts_devel deploy.yml``` 189 | 190 | ```ansible-playbook -i hosts_preprod deploy.yml``` 191 | 192 | ```ansible-playbook -i hosts_prod deploy.yml``` 193 | 194 | Hooks: Custom tasks 195 | ------------------- 196 | 197 | You will typically need to reload your webserver after the `Symlink` step, or download your dependencies before `Code update` or even do it in production before the `Symlink`. So, in order to perform your custom tasks you have some hooks that Ansistrano will execute before and after each of the main 3 steps. **This is the main benefit against other similar deployment roles.** 198 | 199 | ``` 200 | -- /my-local-machine/my-app.com 201 | |-- hosts 202 | |-- deploy.yml 203 | |-- my-custom-tasks 204 | | |-- before-code-update.yml 205 | | |-- after-code-update.yml 206 | | |-- before-symlink.yml 207 | | |-- after-symlink.yml 208 | | |-- before-cleanup.yml 209 | | |-- after-cleanup.yml 210 | ``` 211 | 212 | For example, in order to restart apache after `Symlink` step, we'll add in the `after-symlink.yml` 213 | 214 | ``` 215 | - name: Restart Apache 216 | service: name=httpd state=reloaded 217 | ``` 218 | 219 | * **Q: Where would you add sending email notification after a deployment?** 220 | * **Q: (for PHP and Symfony developers) Where would you clean the cache?** 221 | 222 | You can specify a custom tasks file for before and after every step using `ansistrano_before_*_tasks_file` and `ansistrano_after_*_tasks_file` role variables. See "Role Variables" for more information. 223 | 224 | Variables in custom tasks 225 | ------------------------- 226 | 227 | When writing your custom tasks files you may need some variables that Ansistrano makes available to you: 228 | 229 | * ```{{ ansistrano_timestamp.stdout }}```: Timestamp for the current deployment 230 | * ```{{ ansistrano_release_path.stdout }}```: Path to current deployment release (probably the one you are going to use the most) 231 | * ```{{ ansistrano_releases_path.stdout }}```: Path to releases folder 232 | * ```{{ ansistrano_shared_path.stdout }}```: Path to shared folder (where common releases assets can be stored) 233 | 234 | Pruning old releases 235 | -------------------- 236 | 237 | In continuous delivery environments, you will possibly have a high number of releases in production. Maybe you have tons of space and you don't mind, but it's common practice to keep just a custom number of releases. 238 | 239 | After the deployment, if you want to remove old releases just set the `ansistrano_keep_releases` variable to the total number of releases you want to keep. 240 | 241 | Let's see three deployments with an `ansistrano_keep_releases: 2` configuration: 242 | 243 | ``` 244 | -- /var/www/my-app.com 245 | |-- current -> /var/www/my-app.com/releases/20100509145325 246 | |-- releases 247 | | |-- 20100509145325 248 | |-- shared 249 | ``` 250 | 251 | ``` 252 | -- /var/www/my-app.com 253 | |-- current -> /var/www/my-app.com/releases/20100509150741 254 | |-- releases 255 | | |-- 20100509150741 256 | | |-- 20100509145325 257 | |-- shared 258 | ``` 259 | 260 | ``` 261 | -- /var/www/my-app.com 262 | |-- current -> /var/www/my-app.com/releases/20100512131539 263 | |-- releases 264 | | |-- 20100512131539 265 | | |-- 20100509150741 266 | |-- shared 267 | ``` 268 | 269 | See how the release `20100509145325` has been removed. 270 | 271 | Example Playbook 272 | ---------------- 273 | 274 | In the folder, `example` you can check an example project that shows how to deploy with Ansistrano. In order to run it, you should: 275 | 276 | ``` 277 | $ cd example 278 | $ ansible-playbook -i hosts deploy.yml 279 | ``` 280 | 281 | Sample projects 282 | --------------- 283 | 284 | We have added Ansistrano support for other projects we are working on. 285 | 286 | * LastWishes: Domain-Driven Design PHP Sample App: https://github.com/dddinphp/last-wishes 287 | 288 | As an example, see the execution log of the LastWishes deployment: 289 | 290 | ``` 291 | PLAY [Deploy last wishes app to my server] ************************************ 292 | 293 | GATHERING FACTS *************************************************************** 294 | ok: [quepimquepam.com] 295 | 296 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure deployment base path exists] *** 297 | ok: [quepimquepam.com] 298 | 299 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure releases folder exists] *** 300 | ok: [quepimquepam.com] 301 | 302 | TASK: [carlosbuenosvinos.ansistrano-deploy | Ensure shared elements folder exists] *** 303 | ok: [quepimquepam.com] 304 | 305 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get release timestamp] *********** 306 | changed: [quepimquepam.com] 307 | 308 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get release path] **************** 309 | changed: [quepimquepam.com] 310 | 311 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get releases path] *************** 312 | changed: [quepimquepam.com] 313 | 314 | TASK: [carlosbuenosvinos.ansistrano-deploy | Get shared path (in rsync case)] *** 315 | changed: [quepimquepam.com] 316 | 317 | TASK: [carlosbuenosvinos.ansistrano-deploy | Rsync application files to remote shared copy (in rsync case)] *** 318 | changed: [quepimquepam.com -> 127.0.0.1] 319 | 320 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy existing code to servers] *** 321 | changed: [quepimquepam.com] 322 | 323 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy existing code to remote servers] *** 324 | skipping: [quepimquepam.com] 325 | 326 | TASK: [carlosbuenosvinos.ansistrano-deploy | Update remote repository] ******** 327 | skipping: [quepimquepam.com] 328 | 329 | TASK: [carlosbuenosvinos.ansistrano-deploy | Export a copy of the repo] ******* 330 | skipping: [quepimquepam.com] 331 | 332 | TASK: [carlosbuenosvinos.ansistrano-deploy | Deploy code from to servers] ***** 333 | skipping: [quepimquepam.com] 334 | 335 | TASK: [carlosbuenosvinos.ansistrano-deploy | Copy release version into REVISION file] *** 336 | changed: [quepimquepam.com] 337 | 338 | TASK: [carlosbuenosvinos.ansistrano-deploy | Touches up the release code] ***** 339 | changed: [quepimquepam.com] 340 | 341 | TASK: [carlosbuenosvinos.ansistrano-deploy | Change softlink to new release] *** 342 | changed: [quepimquepam.com] 343 | 344 | TASK: [carlosbuenosvinos.ansistrano-deploy | Reload Apache] ******************* 345 | changed: [quepimquepam.com] 346 | 347 | TASK: [carlosbuenosvinos.ansistrano-deploy | Clean up releases] *************** 348 | skipping: [quepimquepam.com] 349 | 350 | PLAY RECAP ******************************************************************** 351 | quepimquepam.com : ok=14 changed=10 unreachable=0 failed=0 352 | ``` 353 | 354 | License 355 | ------- 356 | 357 | MIT 358 | 359 | Other resources 360 | --------------- 361 | 362 | * [Thoughts on deploying with Ansible](http://www.future500.nl/articles/2014/07/thoughts-on-deploying-with-ansible/) 363 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Path where the code must be deployed to 3 | ansistrano_deploy_to: "/var/www/my-app" 4 | 5 | # Folder name for the releases 6 | ansistrano_version_dir: "releases" 7 | 8 | # Softlink name for the current release 9 | ansistrano_current_dir: "current" 10 | 11 | # Remove rolled back release? 12 | ansistrano_remove_rolled_back: yes 13 | 14 | # Sends anonymous stats to the www.ansistrano.com servers 15 | # You can disallow it by just setting this parameter to "no" in your playbook 16 | ansistrano_allow_anonymous_stats: yes 17 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/meta/.galaxy_install_info: -------------------------------------------------------------------------------- 1 | {install_date: 'Thu May 19 09:17:27 2016', version: 1.4.1} 2 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: ansistrano 4 | description: Ansible role to roll back scripting applications like PHP, Python, Ruby, etc. in a Capistrano style 5 | company: Ansistrano 6 | license: MIT 7 | min_ansible_version: 1.6 8 | platforms: 9 | - name: EL 10 | versions: 11 | - all 12 | - name: GenericUNIX 13 | versions: 14 | - all 15 | - name: Fedora 16 | versions: 17 | - all 18 | - name: opensuse 19 | versions: 20 | - all 21 | - name: Amazon 22 | versions: 23 | - all 24 | - name: GenericBSD 25 | versions: 26 | - all 27 | - name: FreeBSD 28 | versions: 29 | - all 30 | - name: Ubuntu 31 | versions: 32 | - all 33 | - name: SLES 34 | versions: 35 | - all 36 | - name: GenericLinux 37 | versions: 38 | - all 39 | - name: Debian 40 | versions: 41 | - all 42 | categories: 43 | - cloud 44 | - web 45 | dependencies: [] 46 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/anon-stats.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Sends anonymous stats if the user is ok with it 3 | - name: ANSISTRANO | Send anonymous stats 4 | uri: 5 | url: http://ansistrano.com/rollback 6 | method: POST 7 | timeout: 5 8 | when: ansistrano_allow_anonymous_stats 9 | run_once: true 10 | ignore_errors: yes 11 | delegate_to: 127.0.0.1 12 | sudo: false 13 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Remove rolled back version 3 | file: 4 | state: absent 5 | path: "{{ ansistrano_releases_path.stdout }}/{{ ansistrano_current_release_version.stdout }}" 6 | when: ansistrano_remove_rolled_back 7 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/empty.yml: -------------------------------------------------------------------------------- 1 | # This file is intentionally left empty and it is used in those Capistrano flow steps 2 | # where you don't need to execute any custom tasks -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Check BC for early adopters 3 | fail: 4 | msg: "ansistrano_custom_tasks_path is not used anymore. Please, check the documentation at https://github.com/ansistrano/deploy" 5 | when: ansistrano_custom_tasks_path is defined 6 | 7 | - include: "{{ ansistrano_before_setup_tasks_file | default('empty.yml') }}" 8 | - include: setup.yml 9 | - include: "{{ ansistrano_after_setup_tasks_file | default('empty.yml') }}" 10 | 11 | - include: "{{ ansistrano_before_symlink_tasks_file | default('empty.yml') }}" 12 | - include: symlink.yml 13 | - include: "{{ ansistrano_after_symlink_tasks_file | default('empty.yml') }}" 14 | 15 | - include: "{{ ansistrano_before_cleanup_tasks_file | default('empty.yml') }}" 16 | - include: cleanup.yml 17 | - include: "{{ ansistrano_after_cleanup_tasks_file | default('empty.yml') }}" 18 | 19 | - include: anon-stats.yml 20 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/setup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Get releases path 3 | command: echo "{{ ansistrano_deploy_to }}/{{ ansistrano_version_dir }}" 4 | register: ansistrano_releases_path 5 | 6 | - name: ANSISTRANO | Get number of releases 7 | shell: echo `ls {{ ansistrano_releases_path.stdout }} -1t | wc -l` 8 | register: ansistrano_versions_count 9 | 10 | - name: ANSISTRANO | Check if there is more than one release 11 | fail: 12 | msg: "Could not roll back the code because there is no prior release" 13 | when: ansistrano_versions_count.stdout|int <= 1 14 | 15 | - name: ANSISTRANO | Get current release version 16 | shell: echo `ls {{ ansistrano_releases_path.stdout }} -1t | head -n 1` 17 | register: ansistrano_current_release_version 18 | 19 | - name: ANSISTRANO | Get previous releases version 20 | shell: echo `ls {{ ansistrano_releases_path.stdout }} -1t | head -n 2 | tail -n 1` 21 | register: ansistrano_previous_release_version 22 | 23 | - name: ANSISTRANO | Get release path 24 | command: echo "{{ ansistrano_releases_path.stdout }}/{{ ansistrano_previous_release_version.stdout }}" 25 | register: ansistrano_release_path 26 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/tasks/symlink.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: ANSISTRANO | Change web softlink from current release to previous one 3 | file: 4 | state: link 5 | path: "{{ ansistrano_deploy_to }}/{{ ansistrano_current_dir }}" 6 | src: "./{{ ansistrano_version_dir }}/{{ ansistrano_previous_release_version.stdout }}" 7 | -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/test/roles/local-ansistrano: -------------------------------------------------------------------------------- 1 | ../../. -------------------------------------------------------------------------------- /roles/carlosbuenosvinos.ansistrano-rollback/test/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Given no previous deploy 3 | hosts: all 4 | vars: 5 | ansistrano_deploy_to: "/tmp/my-app.com" 6 | tasks: 7 | - name: Assert ansistrano_deploy_to path does not exist 8 | stat: 9 | path: "{{ ansistrano_deploy_to }}" 10 | register: st 11 | - debug: 12 | msg: "Path does not exist and is a directory" 13 | when: st.stat.exists is defined and not st.stat.exists 14 | 15 | - name: When rolling back 16 | hosts: all 17 | vars: 18 | ansistrano_deploy_to: "/tmp/my-app.com" 19 | roles: 20 | - { role: local-ansistrano } 21 | 22 | - name: Then an error should be throw 23 | hosts: all 24 | vars: 25 | ansistrano_deploy_to: "/tmp/my-app.com" 26 | tasks: 27 | - name: Assert ansistrano_deploy_to path does exist 28 | stat: 29 | path: "{{ ansistrano_deploy_to }}" 30 | register: st 31 | - debug: 32 | msg: "Path exists and is a directory" 33 | when: st.stat.exists is defined and st.stat.exists 34 | --------------------------------------------------------------------------------