Add Dublin Fabric talk
This commit is contained in:
parent
45ee78b607
commit
6165e4a372
7 changed files with 299 additions and 154 deletions
Binary file not shown.
Before ![]() (image error) Size: 42 KiB |
Binary file not shown.
After ![]() (image error) Size: 38 KiB |
Binary file not shown.
After ![]() (image error) Size: 162 KiB |
|
@ -1,8 +1,11 @@
|
||||||
autoscale: true
|
autoscale: true
|
||||||
build-lists: true
|
build-lists: true
|
||||||
theme: poster, 7
|
theme: next, 9
|
||||||
|
|
||||||
# [fit] *Deploying* PHP<br>applications<br>*with* Fabric
|
# [fit] Deploying Drupal <br>with Fabric
|
||||||
|
|
||||||
|
### Oliver Davies
|
||||||
|
### bit.ly/deploying-drupal-fabric
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -16,26 +19,30 @@ theme: poster, 7
|
||||||
|
|
||||||
[.build-lists: false]
|
[.build-lists: false]
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
- Senior Developer at Microserve
|
- Senior Developer at Microserve
|
||||||
- Part-time freelance Developer & Sysadmin
|
- Part-time freelance Developer & System Administrator
|
||||||
|
- Drupal, Symfony, Silex, Laravel, Sculpin
|
||||||
- Drupal Bristol, PHPSW, DrupalCamp Bristol
|
- Drupal Bristol, PHPSW, DrupalCamp Bristol
|
||||||
|
- Sticker collector, herder of elePHPants
|
||||||
- @opdavies
|
- @opdavies
|
||||||
- oliverdavies.uk
|
- oliverdavies.uk
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
^ Drupal (7 & 8), Symfony, Silex, Laravel, Sculpin
|
^ Not a Python Developer
|
||||||
Not a Python Developer
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## *What is* Fabric*?*
|

|
||||||
|
|
||||||
|
## [fit] What is <br>Fabric?
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## What is Fabric?
|
## What is Fabric?
|
||||||
|
|
||||||
*Fabric is a* Python (2.5-2.7) library and command-line tool for streamlining the use of SSH *for application deployment or systems administration tasks.*
|
Fabric is a Python (2.5-2.7) library and command-line tool for streamlining the use of SSH for application deployment or systems administration tasks.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -63,7 +70,7 @@ It provides a basic suite of operations for executing local or remote shell comm
|
||||||
|
|
||||||
- Powerful
|
- Powerful
|
||||||
- Flexible
|
- Flexible
|
||||||
- Easier to write than bash
|
- Easier to read and write than bash
|
||||||
|
|
||||||
^ - Can be used for different languages, frameworks
|
^ - Can be used for different languages, frameworks
|
||||||
- Can be used for small, simple deployments or large, complicated ones
|
- Can be used for small, simple deployments or large, complicated ones
|
||||||
|
@ -86,7 +93,7 @@ $ apt-get install python-fabric
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Writing your <br>first *fabfile*
|
## [fit] Writing your <br>first fabfile
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -159,7 +166,7 @@ from fabric.contrib.files import *
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
|
||||||
def build():
|
def main():
|
||||||
with cd('/var/www/html'):
|
with cd('/var/www/html'):
|
||||||
run('git pull')
|
run('git pull')
|
||||||
run('composer install')
|
run('composer install')
|
||||||
|
@ -170,7 +177,7 @@ def build():
|
||||||
## Task arguments
|
## Task arguments
|
||||||
|
|
||||||
```python, [.highlight: 1, 4-6]
|
```python, [.highlight: 1, 4-6]
|
||||||
def build(run_composer=True):
|
def main(run_composer=True):
|
||||||
with cd('/var/www/html'):
|
with cd('/var/www/html'):
|
||||||
run('git pull')
|
run('git pull')
|
||||||
|
|
||||||
|
@ -183,7 +190,7 @@ def build(run_composer=True):
|
||||||
## Task arguments
|
## Task arguments
|
||||||
|
|
||||||
```python, [.highlight: 1, 4-15]
|
```python, [.highlight: 1, 4-15]
|
||||||
def build(run_composer=True, env='prod', build_type):
|
def main(run_composer=True, env='prod', build_type):
|
||||||
with cd('/var/www/html'):
|
with cd('/var/www/html'):
|
||||||
run('git pull')
|
run('git pull')
|
||||||
|
|
||||||
|
@ -207,7 +214,7 @@ def build(run_composer=True, env='prod', build_type):
|
||||||
|
|
||||||
```python, [.highlight: 4-15]
|
```python, [.highlight: 4-15]
|
||||||
@task
|
@task
|
||||||
def build():
|
def main():
|
||||||
with cd('/var/www/html'):
|
with cd('/var/www/html'):
|
||||||
build()
|
build()
|
||||||
post_install()
|
post_install()
|
||||||
|
@ -269,7 +276,7 @@ Disconnecting from production... done.
|
||||||
|
|
||||||
[.build-lists: false]
|
[.build-lists: false]
|
||||||
|
|
||||||
## *Not* Building on Prod
|
## Not Building on Prod
|
||||||
|
|
||||||
1. Build locally and deploy.
|
1. Build locally and deploy.
|
||||||
|
|
||||||
|
@ -353,7 +360,7 @@ Done.
|
||||||
|
|
||||||
[.build-lists: false]
|
[.build-lists: false]
|
||||||
|
|
||||||
## *Not* Building on Prod
|
## Not Building on Prod
|
||||||
|
|
||||||
1. ~~Build locally and deploy.~~
|
1. ~~Build locally and deploy.~~
|
||||||
1. Build in a separate directory and switch after build.
|
1. Build in a separate directory and switch after build.
|
||||||
|
@ -362,14 +369,14 @@ Done.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deploying into a *different directory*
|
## Deploying into a different directory
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from fabric.api import *
|
from fabric.api import *
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
project_dir = '/var/www/html'
|
project_dir = '/var/www/html'
|
||||||
next_release = "%(time).0f" % { 'time': time() } # timestamp
|
next_release = "%(time).0f" % { 'time': time() } # Current timestamp
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
if not exists(project_dir):
|
if not exists(project_dir):
|
||||||
|
@ -380,7 +387,7 @@ def init():
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deploying into a *different directory*
|
## Deploying into a different directory
|
||||||
|
|
||||||
```python
|
```python
|
||||||
current_release = '%s/%s' % (releases_dir, next_release)
|
current_release = '%s/%s' % (releases_dir, next_release)
|
||||||
|
@ -399,7 +406,7 @@ def build():
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deploying into a *different directory*
|
## Deploying into a different directory
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def pre_build(build_number):
|
def pre_build(build_number):
|
||||||
|
@ -413,7 +420,7 @@ def backup_database():
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Deploying into a *different directory*
|
## Deploying into a different directory
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def update_symlinks():
|
def update_symlinks():
|
||||||
|
@ -429,9 +436,9 @@ def update_symlinks():
|
||||||
[production] Executing task 'main'
|
[production] Executing task 'main'
|
||||||
[production] run: git clone https://github.com/opdavies/oliverdavies.uk.git
|
[production] run: git clone https://github.com/opdavies/oliverdavies.uk.git
|
||||||
/var/www/html/releases/1505865600
|
/var/www/html/releases/1505865600
|
||||||
===> Installing Composer dependencies...
|
Installing Composer dependencies...
|
||||||
[production] run: composer install --no-dev
|
[production] run: composer install --no-dev
|
||||||
===> Update the symlink to the new release...
|
Update the symlink to the new release...
|
||||||
[production] run: ln -nfs /var/www/html/releases/1505865600
|
[production] run: ln -nfs /var/www/html/releases/1505865600
|
||||||
/var/www/html/current
|
/var/www/html/current
|
||||||
|
|
||||||
|
@ -443,7 +450,7 @@ Done.
|
||||||
```bash
|
```bash
|
||||||
# /var/www/html
|
# /var/www/html
|
||||||
|
|
||||||
shared
|
shared # settings.local.php, sites.php, files etc.
|
||||||
releases/1502323200
|
releases/1502323200
|
||||||
releases/1505692800
|
releases/1505692800
|
||||||
releases/1505696400
|
releases/1505696400
|
||||||
|
@ -479,7 +486,91 @@ def main(builds_to_keep=3):
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Is the site still running?
|
## [fit] Does the code still <br>merge cleanly?
|
||||||
|
|
||||||
|
^ Pre-task
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```python
|
||||||
|
def check_for_merge_conflicts(target_branch):
|
||||||
|
with settings(warn_only=True):
|
||||||
|
print('Ensuring that this can be merged into the main branch.')
|
||||||
|
|
||||||
|
if local('git fetch && git merge --no-ff origin/%s'
|
||||||
|
% target_branch).failed:
|
||||||
|
abort('Cannot merge into target branch.')
|
||||||
|
```
|
||||||
|
|
||||||
|
^ Define target branch in Jenkins/when Fabric is run.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## [fit] Do our tests <br>still pass?
|
||||||
|
|
||||||
|
[.footer: http://nashvillephp.org/images/testing-workshop-banner-1600x800.jpg]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```python
|
||||||
|
with settings(warn_only=True):
|
||||||
|
with lcd('%s/docroot/core' % project_dir):
|
||||||
|
if local('../../vendor/bin/phpunit ../modules/custom').failed:
|
||||||
|
abort('Tests failed!')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
[localhost] run: ../../vendor/bin/phpunit ../modules/custom
|
||||||
|
[localhost] out: PHPUnit 4.8.35 by Sebastian Bergmann and contributors.
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: .......
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: Time: 1.59 minutes, Memory: 6.00MB
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: OK (7 tests, 42 assertions)
|
||||||
|
[localhost] out:
|
||||||
|
|
||||||
|
|
||||||
|
Done.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
```
|
||||||
|
[localhost] run: ../../vendor/bin/phpunit ../modules/custom
|
||||||
|
[localhost] out: PHPUnit 4.8.35 by Sebastian Bergmann and contributors.
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: E
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: Time: 18.67 seconds, Memory: 6.00MB
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: There was 1 error:
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: 1) Drupal\Tests\broadbean\Functional\AddJobTest::testNodesAreCreated
|
||||||
|
[localhost] out: Behat\Mink\Exception\ExpectationException: Current response status code is 200, but 201 expected.
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: /var/www/html/vendor/behat/mink/src/WebAssert.php:770
|
||||||
|
[localhost] out: /var/www/html/vendor/behat/mink/src/WebAssert.php:130
|
||||||
|
[localhost] out: /var/www/html/docroot/modules/custom/broadbean/tests/src/Functional/AddJobTest.php:66
|
||||||
|
[localhost] out:
|
||||||
|
[localhost] out: FAILURES!
|
||||||
|
[localhost] out: Tests: 1, Assertions: 6, Errors: 1.
|
||||||
|
[localhost] out:
|
||||||
|
|
||||||
|
Warning: run() received nonzero return code 2 while executing '../../vendor/bin/phpunit ../modules/custom/broadbean'!
|
||||||
|
|
||||||
|
Fatal error: Tests failed!
|
||||||
|
|
||||||
|
Aborting.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [fit] Is the site <br>still running?
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -489,11 +580,11 @@ def main(builds_to_keep=3):
|
||||||
run(command).failed:
|
run(command).failed:
|
||||||
# Fail
|
# Fail
|
||||||
|
|
||||||
run(command).return_code == 0:
|
|
||||||
# Pass
|
|
||||||
|
|
||||||
run(command).return_code == 1:
|
run(command).return_code == 1:
|
||||||
# Fail
|
# Fail
|
||||||
|
|
||||||
|
run(command).return_code == 0:
|
||||||
|
# Pass
|
||||||
```
|
```
|
||||||
|
|
||||||
^ Works for local and remote.
|
^ Works for local and remote.
|
||||||
|
@ -501,12 +592,13 @@ run(command).return_code == 1:
|
||||||
---
|
---
|
||||||
|
|
||||||
```python
|
```python
|
||||||
def post_tasks():
|
print 'Checking the site is alive...'
|
||||||
print '===> Checking the site is alive.'
|
if run('drush status | egrep "Connected|Successful"').failed:
|
||||||
if run('drush status | egrep "Connected|Successful"').failed:
|
|
||||||
# Revert back to previous build.
|
# Revert back to previous build.
|
||||||
```
|
```
|
||||||
|
|
||||||
|
^ egrep is an acronym for "Extended Global Regular Expressions Print". It is a program which scans a specified file line by line, returning lines that contain a pattern matching a given regular expression.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
```bash, [.highlight 3]
|
```bash, [.highlight 3]
|
||||||
|
@ -527,30 +619,31 @@ PHP configuration : /etc/php5/cli/php.ini
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
^ "Database" to "PHP configuration" missing if cannot connect.
|
^ Successful
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Does the code still merge cleanly?
|
```bash, [.highlight 3]
|
||||||
|
$ drush status
|
||||||
|
|
||||||
^ Pre-task
|
Drupal version : 8.3.7
|
||||||
|
Site URI : http://default
|
||||||
---
|
Database driver : mysql
|
||||||
|
Database hostname : db
|
||||||
```python
|
Database username : user
|
||||||
def check_for_merge_conflicts(target_branch):
|
Database name : default
|
||||||
with settings(warn_only=True):
|
PHP configuration : /etc/php5/cli/php.ini
|
||||||
print('===> Ensuring that this can be merged into the main branch.')
|
...
|
||||||
|
|
||||||
if local('git fetch && git merge --no-ff origin/%s' % target_branch).failed:
|
|
||||||
abort('Cannot merge into target branch.')
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
^ Failed.
|
||||||
|
"Database" to "PHP configuration" missing if cannot connect or DB is empty.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Making fabric smarter
|
## [fit] Making Fabric <br>Smarter
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
@ -597,11 +690,14 @@ drupal:
|
||||||
import: yes
|
import: yes
|
||||||
name: sync
|
name: sync
|
||||||
cmi_tools: no
|
cmi_tools: no
|
||||||
|
tests:
|
||||||
|
simpletest: false
|
||||||
|
phpunit: true
|
||||||
theme:
|
theme:
|
||||||
path: 'themes/custom/drupalbristol'
|
path: 'themes/custom/drupalbristol'
|
||||||
build:
|
build:
|
||||||
npm: no
|
|
||||||
type: gulp
|
type: gulp
|
||||||
|
npm: no
|
||||||
yarn: yes
|
yarn: yes
|
||||||
|
|
||||||
composer:
|
composer:
|
||||||
|
@ -618,7 +714,10 @@ composer:
|
||||||
from fabric.api import *
|
from fabric.api import *
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
with open('app.yml', 'r') as file:
|
config = []
|
||||||
|
|
||||||
|
if exists('app.yml'):
|
||||||
|
with open('app.yml', 'r') as file:
|
||||||
config = yaml.load(file.read())
|
config = yaml.load(file.read())
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -640,19 +739,62 @@ if config['composer']['install'] == True:
|
||||||
```python
|
```python
|
||||||
# fabfile.py
|
# fabfile.py
|
||||||
|
|
||||||
|
if build_type == 'drupal':
|
||||||
|
drupal = config['drupal']
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project settings file
|
||||||
|
|
||||||
|
```python, [.highlight: 6-9]
|
||||||
|
# fabfile.py
|
||||||
|
|
||||||
|
if build_type == 'drupal':
|
||||||
|
drupal = config['drupal']
|
||||||
|
|
||||||
|
with cd(drupal['root']):
|
||||||
|
if drupal['version'] == 8:
|
||||||
|
|
||||||
|
if drupal['version'] == 7:
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project settings file
|
||||||
|
|
||||||
|
```python, [.highlight: 8-10]
|
||||||
|
# fabfile.py
|
||||||
|
|
||||||
if build_type == 'drupal':
|
if build_type == 'drupal':
|
||||||
drupal = config['drupal']
|
drupal = config['drupal']
|
||||||
|
|
||||||
with cd(drupal['root']):
|
with cd(drupal['root']):
|
||||||
if drupal['version'] == 8:
|
if drupal['version'] == 8:
|
||||||
if drupal['config']['import'] == True:
|
if drupal['config']['import'] == True:
|
||||||
if drupal['config']['cmi_tools']:
|
# Import the staged configuration.
|
||||||
run('drush cim -y %s' % drupal['config']['import']['name'])
|
run('drush cim -y %s' % drupal['config']['name'])
|
||||||
else:
|
```
|
||||||
run('drush cimy -y %s' % drupal['config']['import']['name'])
|
|
||||||
|
|
||||||
if drupal['version'] == 7:
|
---
|
||||||
...
|
|
||||||
|
## Project settings file
|
||||||
|
|
||||||
|
```python, [.highlight: 9-15]
|
||||||
|
# fabfile.py
|
||||||
|
|
||||||
|
if build_type == 'drupal':
|
||||||
|
drupal = config['drupal']
|
||||||
|
|
||||||
|
with cd(drupal['root']):
|
||||||
|
if drupal['version'] == 8:
|
||||||
|
if drupal['config']['import'] == True:
|
||||||
|
if drupal['config']['cmi_tools'] == True:
|
||||||
|
# Use Drush CMI Tools.
|
||||||
|
run('drush cimy -y %s' % drupal['config']['name'])
|
||||||
|
else:
|
||||||
|
# Use core.
|
||||||
|
run('drush cim -y %s' % drupal['config']['name'])
|
||||||
```
|
```
|
||||||
|
|
||||||
^ - Less hard-coded values
|
^ - Less hard-coded values
|
||||||
|
@ -665,6 +807,8 @@ if build_type == 'drupal':
|
||||||
## Project settings file
|
## Project settings file
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
# fabfile.py
|
||||||
|
|
||||||
theme = config['theme']
|
theme = config['theme']
|
||||||
|
|
||||||
with cd(theme['path']):
|
with cd(theme['path']):
|
||||||
|
@ -713,7 +857,8 @@ for hook in config['commands'].get('deploy', '').split("\n"):
|
||||||
|
|
||||||
## Other things
|
## Other things
|
||||||
|
|
||||||
- Run Drush/console/artisan commands
|
- Run Drush commands
|
||||||
|
- Run automated tests
|
||||||
- Verify file permissions
|
- Verify file permissions
|
||||||
- Restart services
|
- Restart services
|
||||||
- Anything you can do on the command line...
|
- Anything you can do on the command line...
|
||||||
|
@ -732,17 +877,17 @@ for hook in config['commands'].get('deploy', '').split("\n"):
|
||||||
|
|
||||||
[.build-lists: false]
|
[.build-lists: false]
|
||||||
|
|
||||||
- https://www.oliverdavies.uk/talks/deploying-php-fabric
|
- https://www.oliverdavies.uk/talks/deploying-drupal-fabric
|
||||||
- http://fabfile.org
|
- http://fabfile.org
|
||||||
- https://github.com/opdavies/fabric-example-sculpin
|
|
||||||
- https://github.com/opdavies/fabric-example-drupal
|
- https://github.com/opdavies/fabric-example-drupal
|
||||||
|
- https://github.com/opdavies/fabric-example-sculpin
|
||||||
- https://deploy.serversforhackers.com (~~$129~~ $79)
|
- https://deploy.serversforhackers.com (~~$129~~ $79)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## *joind.in/talk/*4e35d
|
## Thanks!
|
||||||
|
|
||||||
---
|
# Questions?
|
||||||
|
|
||||||
## @opdavies
|
### @opdavies
|
||||||
## *oliverdavies.uk*
|
### oliverdavies.uk
|
||||||
|
|
Binary file not shown.
BIN
me-phpnw.png
BIN
me-phpnw.png
Binary file not shown.
Before ![]() (image error) Size: 873 KiB After ![]() (image error) Size: 177 KiB ![]() ![]() |
BIN
me-phpnw2.png
BIN
me-phpnw2.png
Binary file not shown.
Before ![]() (image error) Size: 172 KiB |
Loading…
Add table
Add a link
Reference in a new issue