Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Cleanup & GitHub OAuth Token

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $9.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

It's time to polish our deploy. Right now, you can surf to the /app_dev.php script on production. You can't really access it... but that file should not be deployed.

Back on the Ansistrano docs, look at the workflow diagram. So far, we've been hooking into "After Symlink Shared", because that's when the site is basically functional but not yet live. To delete app_dev.php, let's hook into "Before Symlink". It's basically the same, but this is the last opportunity to do something right before the deploy becomes live.

Scroll down to the variables section and copy ansistrano_before_symlink_tasks_file. In deploy.yml, paste that and set it to a new file: before-symlink.yml:

---
- hosts: aws
... lines 3 - 14
vars:
... lines 16 - 48
# Hooks: custom tasks if you need them
... line 50
ansistrano_before_symlink_tasks_file: "{{ playbook_dir }}/deploy/before-symlink.yml"
... lines 52 - 58

In the deploy/ directory, create that! We only need one new task: "Remove sensitive scripts from web/ dir". Use the file module:

---
- name: Remove sensitive scripts from web/ dir
file:
... lines 4 - 9

For path, first go back to deploy.yml, create a new variable release_web_path and set it to {{ ansistrano_release_path.stdout }}/web:

---
- hosts: aws
... lines 3 - 14
vars:
... lines 16 - 18
release_web_path: "{{ ansistrano_release_path.stdout }}/web"
... lines 20 - 58

Copy that variable and get back to work! Set path to {{ release_web_path }}/{{ item }}:

---
- name: Remove sensitive scripts from web/ dir
file:
path: '{{ release_web_path }}/{{ item }}'
... lines 5 - 9

We're also going to delete this config.php script:

423 lines web/config.php
<?php
/*
* ************** CAUTION **************
*
* DO NOT EDIT THIS FILE as it will be overridden by Composer as part of
* the installation/update process. The original file resides in the
* SensioDistributionBundle.
*
* ************** CAUTION **************
*/
... lines 12 - 423

Set state to absent and add with_items. Delete 2: app_dev.php and config.php:

---
- name: Remove sensitive scripts from web/ dir
file:
path: '{{ release_web_path }}/{{ item }}'
state: absent
with_items:
- app_dev.php
- config.php

Oh, and since I never deployed my services.yml change, let's commit these changes, push, and deploy to the cloud!

ansible-playbook ansible/deploy.yml -i ansible/hosts.ini --ask-vault-pass

Composer GitHub Access Token

While we're waiting, there is one thing that could break our deploy: GitHub rate limiting. If composer install accesses the GitHub API too often, the great and powerful GitHub monster will kill our deploy! This shouldn't happen, thanks to Composer's caching, but it is possible.

Tip

Actually, a change made to Composer in 2016 effectively fixed the rate limiting problem. But the fix (GitHub OAuth token) we will show will allow you to install dependencies from private repositories.

Google for "Composer GitHub token" to find a spot on their troubleshooting docs called API rate limit and OAuth tokens. All we need to do is create a personal access token on GitHub and then run this command on the server. This will please and pacify the GitHub monster, and the rate limiting problem will be gone.

Click the Create link and then "Generate new token". Think of a clever name and give it repo privileges.

Setting the GitHub Token in Ansible

Perfect! We could run the composer config command manually on the server. But instead, let's do it in our provision playbook: ansible/playbook.yml.

This is pretty easy... except that we probably don't want to hardcode my access token. Instead, we'll use the Ansible vault: a new vault just for playbook.yml. As soon as the deploy finishes, create it:

ansible-vault create ansible/vars/provision_vault.yml

Use the normal beefpass as the password. And then, add just one variable: vault_github_oauth_token set to the new access token:

# ansible/vars/provision_vault.yml

vault_github_oauth_token: 146f9e4f876164866d5afd956843d9141c4c6c47

Save and close! Whenever I have a vault, I also like to create a simple variables file. Create provision_vars.yml. And inside, set github_oauth_token to vault_github_oauth_token:

---
github_oauth_token: "{{ vault_github_oauth_token }}"

Finally, in playbook.yml, let's include these! Include ./vars/provision_vault.yml and then ./vars/provision_vars.yml:

---
- hosts: webserver
vars_files:
- ./vars/provision_vault.yml
- ./vars/provision_vars.yml
- ./vars/vars.yml
... lines 8 - 182

We now have access to the github_oauth_token variable.

We have a few tasks that install the Composer executable:

---
- hosts: webserver
... lines 3 - 35
tasks:
... lines 37 - 100
- name: Check for Composer
stat:
path: /usr/local/bin/composer
register: composer_stat
- name: Download Composer
script: scripts/install_composer.sh
when: not composer_stat.stat.exists
- name: Move Composer globally
become: true
command: mv composer.phar /usr/local/bin/composer
when: not composer_stat.stat.exists
- name: Set permissions on Composer
become: true
file:
path: /usr/local/bin/composer
mode: "a+x"
- name: Make sure Composer is at its latest version
composer:
working_dir: "/home/{{ ansible_user }}"
command: self-update
register: composer_self_update
changed_when: "not composer_self_update.stdout|search('You are already using composer version')"
... lines 127 - 182

After those, create a new one: "Set GitHub OAuth token for Composer". Use the composer module and set command to config:

---
- hosts: webserver
... lines 3 - 35
tasks:
... lines 37 - 100
- name: Check for Composer
stat:
path: /usr/local/bin/composer
register: composer_stat
- name: Download Composer
script: scripts/install_composer.sh
when: not composer_stat.stat.exists
- name: Move Composer globally
become: true
command: mv composer.phar /usr/local/bin/composer
when: not composer_stat.stat.exists
- name: Set permissions on Composer
become: true
file:
path: /usr/local/bin/composer
mode: "a+x"
- name: Make sure Composer is at its latest version
composer:
working_dir: "/home/{{ ansible_user }}"
command: self-update
register: composer_self_update
changed_when: "not composer_self_update.stdout|search('You are already using composer version')"
- name: Set GitHub OAuth token for Composer
composer:
command: config
... lines 131 - 182

The docs show the full command we need. Copy the arguments and set arguments to that string. Replace the <oauthtoken> part with {{ github_oauth_token }}:

---
- hosts: webserver
... lines 3 - 35
tasks:
... lines 37 - 127
- name: Set GitHub OAuth token for Composer
composer:
command: config
arguments: '-g github-oauth.github.com "{{ github_oauth_token }}"'
... lines 132 - 182

Also set working_dir to /home/{{ ansible_user }}... the composer module requires this to be set. And at the end, add a tag: github_oauth:

---
- hosts: webserver
... lines 3 - 35
tasks:
... lines 37 - 127
- name: Set GitHub OAuth token for Composer
composer:
command: config
arguments: '-g github-oauth.github.com "{{ github_oauth_token }}"'
working_dir: "/home/{{ ansible_user }}"
tags:
- github_oauth
... lines 135 - 182

Why the tag? Because I really don't want to re-run my entire provision playbook just for this task. Translation: I'm being lazy! Run the provision playbook, but with an extra -t github_oauth, just this one time:

ansible-playbook ansible/playbook.yml -i ansible/hosts.ini --ask-vault-pass -l aws -t github_oauth

Use beefpass! Great! So... is this working?

On GitHub, you can see that the token has never been used. When we deploy, composer install should now use it. But first, back on the server, run composer clear-cache:

composer clear-cache

to make sure it actually makes some API requests and doesn't just load everything from cache.

Now, deploy!

ansible-playbook ansible/deploy.yml -i ansible/hosts.ini --ask-vault-pass

As soon as this executes the "Composer install" task, our access key should be used. There it is... and yes! The key was used within the last week. Now we will never have rate limiting issues.

Leave a comment!

0
Login or Register to join the conversation
Cat in space

"Houston: no signs of life"
Start the conversation!

While the fundamentals of Ansistrano haven't changed, this tutorial is built using Symfony 3, which has significant differences versus Symfony 4 and later.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "doctrine/doctrine-bundle": "^1.6", // 1.6.8
        "doctrine/orm": "^2.5", // v2.7.2
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "sensio/distribution-bundle": "^5.0.19", // v5.0.20
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.26
        "symfony/monolog-bundle": "^3.1.0", // v3.1.0
        "symfony/polyfill-apcu": "^1.0", // v1.4.0
        "symfony/swiftmailer-bundle": "^2.3.10", // v2.6.3
        "symfony/symfony": "3.3.*", // v3.3.5
        "twig/twig": "^1.0||^2.0", // v1.34.4
        "doctrine/doctrine-migrations-bundle": "^1.2", // v1.2.1
        "predis/predis": "^1.1", // v1.1.1
        "composer/package-versions-deprecated": "^1.11" // 1.11.99
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.1.6
        "symfony/phpunit-bridge": "^3.0", // v3.3.5
        "doctrine/data-fixtures": "^1.1", // 1.3.3
        "hautelook/alice-bundle": "^1.3" // v1.4.1
    }
}

What Ansible libraries does this tutorial use?

# ansible/requirements.yml
-
    src: DavidWittman.redis
    version: 1.2.4
-
    src: ansistrano.deploy
    version: 2.7.0
-
    src: ansistrano.rollback
    version: 2.0.1
userVoice