Rename directories

This commit is contained in:
Oliver Davies 2020-03-14 22:35:59 +00:00
parent ad22d86a2e
commit 49b98d0681
211 changed files with 0 additions and 0 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 626 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

View file

@ -0,0 +1,948 @@
autoscale: true
build-lists: true
code: line-height(1.2)
header-emphasis: #53B0EB
text: alignment(left)
theme: simple, 8
# [fit] **Deploying PHP applications** <br>using Ansible, Ansible Vault <br>and Ansistrano
^ I work primarily with PHP, and there will be some PHP-isms in this talk (LAMP stack, Composer).
Will be using a Drupal 8 application as the example, but the tools are tool and language agnostic.
---
# [fit] **Deploying ~~PHP~~ applications** <br>using Ansible, Ansible Vault <br>and Ansistrano
---
## **What we'll be looking at**
* **Ansible** crash course
* Keeping secrets with **Ansible Vault**
* Deployments with **Ansistrano**
---
[.build-lists: false]
[.header: #111111]
![right 500%](../images/me-phpnw-inviqa.jpg)
- Full Stack Software Developer & System Administrator
- **Senior Software Engineer** at **Inviqa**
- Acquia certified **Drupal 8 Grand Master** and **Cloud Pro**
- Open sourcer
- Drupal 7 & 8 **core contributor**
- @opdavies
- www.oliverdavies.uk
^ Maintain Drupal modules, PHP CLI tools and libraries, Ansible roles
Blog on my website
I work primarily with Drupal and Symfony
I work for Inviqa, but this based on my personal and side projects.
I've been using Ansible for a number of years, initially only for provisioning and setting up my laptop, and later for application deployments
---
![350% inline](images/logo-platformsh.png)
![150% inline](images/logo-acquia.png)
![130% inline](images/logo-pantheon.png)
^ Large, well-known managed hosting companies
Optimised servers for PHP/Drupal applications
Include some sort of deployment system
This workflow doesn't apply to this scenario
---
![100%](images/logo-digital-ocean.png)
![30%](images/logo-linode.png)
![30%](images/logo-vultr.png)
^ More applicable to virtual or dedicated servers with no existing deployment process
Not enough budget for fully-managed, or using internal infrastructure
This is where the this workflow would be useful
---
# **What is Ansible?**
---
## Ansible is an open-source **software provisioning, configuration management, and application-deployment** tool.
![10% right](images/ansible.png)
[.footer: https://en.wikipedia.org/wiki/Ansible_(software)]
---
![10% right](images/ansible.png)
### **What is Ansible?**
* CLI tool
* Written in Python
* Configured with YAML
* Executes ad-hoc remote commands
* Installs software packages
* Performs deployment steps
* Batteries included
^ Written in Python but you don't need to write or know Python to use it
Drupal, Symfony and a lot of other projects use YAML
First-party modules (SSH keys, file and directory management, package repositories, stopping/starting/restarting services, DO/Linode/AWS integration)
---
![10% right](images/ansible.png)
### **What is Ansible?**
* Hosts/Inventories
* Commands
* Playbooks
* Tasks
* Roles
^ Hosts: where your managed nodes/hosts are. Can be static or dynamic.
Commands: run from a control node onto managed nodes
Playbooks and Tasks: YAML representation of a series of commands/steps
---
![10% right](images/ansible.png)
### **Why Ansible?**
* Familiar syntax
* Easily readable
* No server dependencies
* Easy to add to an existing project
* Includes relevant modules (e.g. Composer)
* Idempotency
^ Drupal 8, Symfony, Ansible all use YAML
Runs on any server with Python
Plugins into Drupal via CLI apps like Drush and Drupal Console
Changes are only made when needed (once)
---
# **Hosts / Inventories**
---
```ini
# hosts.ini
[webservers]
192.168.33.10
```
^ Supports wildcards and ranges.
---
```yaml
# hosts.yml
webservers:
hosts:
192.168.33.10:
```
---
# **Commands**
---
# `ansible all -i hosts.yml -m ping`
---
```json
webservers | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
```
---
# `ansible all `<br>`-i hosts.yml `<br>`-m command `<br>`-a 'git pull `<br>`--chdir=/app'`
---
# `ansible all -i hosts.yml`<br>`-m git -a 'repo=https://github.com/opdavies/dransible dest=/app`'
---
# **Tasks and Playbooks**
---
```yaml
# playbook.yml
---
- hosts: webservers # or 'all'
vars:
git_repo: https://github.com/opdavies/dransible
tasks:
- name: Update the code
git:
repo: '{{ git_repo }}'
dest: /app
version: master
update: true
```
---
# `ansible-playbook `<br>`playbook.yml -i hosts.yml`
---
# **Roles: <br>configuring a LAMP stack**
^ Collections of tasks, variables and handlers
---
```yaml
# requirements.yml
---
- src: geerlingguy.apache
- src: geerlingguy.composer
- src: geerlingguy.mysql
- src: geerlingguy.php
- src: geerlingguy.php-mysql
```
^ Provisioning LAMP stack and Composer
---
# `ansible-galaxy -r`<br>`requirements.yml install`
---
```yaml
# provision.yml
---
- hosts: webservers
roles:
- geerlingguy.apache
- geerlingguy.mysql
- geerlingguy.php
- geerlingguy.php-mysql
- geerlingguy.composer
```
^ Role order matters!
---
```yaml
# provision.yml
---
- hosts: webservers
# ...
vars:
apache_vhosts:
- servername: dransible.wip
documentroot: /app/web
```
---
```yaml
# provision.yml
---
- hosts: webservers
# ...
vars:
# ...
php_version: '7.4'
php_packages_extra:
- libapache2-mod-php{{ php_version }}
- libpcre3-dev
```
---
```yaml
# provision.yml
---
- hosts: webservers
# ...
vars:
# ...
mysql_databases:
- name: main
mysql_users:
- name: user
password: secret
priv: main.*:ALL
```
---
# `ansible-playbook provision.yml -i hosts.yml`
---
```
PLAY [Provision the webserver machines] ********************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [webservers]
TASK [geerlingguy.apache : Include OS-specific variables.] *************************************************************
ok: [webservers]
TASK [geerlingguy.apache : Include variables for Amazon Linux.]
skipping: [webservers]
TASK [geerlingguy.apache : Define apache_packages.] ********************************************************************
ok: [webservers]
TASK [geerlingguy.apache : include_tasks] ******************************************************************************
included: /Users/opdavies/.ansible/roles/geerlingguy.apache/tasks/setup-Debian.yml for webservers
TASK [geerlingguy.apache : Update apt cache.] **************************************************************************
changed: [webservers]
```
---
```
TASK [geerlingguy.composer : Ensure composer directory exists.] ********************************************************
ok: [webservers]
TASK [geerlingguy.composer : include_tasks] ****************************************************************************
skipping: [webservers]
TASK [geerlingguy.composer : include_tasks] ****************************************************************************
skipping: [webservers]
RUNNING HANDLER [geerlingguy.apache : restart apache] ******************************************************************
changed: [webservers]
RUNNING HANDLER [geerlingguy.mysql : restart mysql] ********************************************************************
changed: [webservers]
RUNNING HANDLER [geerlingguy.php : restart webserver] ******************************************************************
changed: [webservers]
RUNNING HANDLER [geerlingguy.php : restart php-fpm] ********************************************************************
skipping: [webservers]
PLAY RECAP *************************************************************************************************************
webservers : ok=111 changed=32 unreachable=0 failed=0 skipped=78 rescued=0 ignored=0
```
---
![70%](images/after-provision-1.png)
---
![70%](images/after-provision-2.png)
---
# **Keeping secrets with <br>Ansible Vault**
---
```yaml
# provision.yml
---
- hosts: webservers
# ...
vars:
# ...
mysql_databases:
- name: main
mysql_users:
- name: user
password: secret
priv: main.*:ALL
```
---
[.code-highlight: 11-14]
```yaml
# provision.yml
---
- hosts: webservers
# ...
vars:
# ...
mysql_databases:
- name: main
mysql_users:
- name: user
password: secret
priv: main.*:ALL
```
---
# `ansible-vault create` <br>`vault.yml`
---
```yaml
# vars/vault.yml
---
vault_database_name: main
vault_database_user: user
vault_database_password: secret
```
^ Optional, but easier to see where variables are set
---
```
$ANSIBLE_VAULT;1.1;AES256
36656233323539616336393838396137343939623233393338666530313
73037323366326363306531336333353163643063663335396139363762
39383133330a35636566623262353733373066363837393264616134613
16366376266646437373366373738303931616333626332353839353332
32663432346662613438330a38643539343232376138613733373636343
63864666430313866623539333039363138646331326565386263386666
35306264396230633939346532356665306564626431353936643135376
23834346635366637613235656165643361316663396530383263333064
33326264316235396431666262346637366563376330363238373331373
43533386165366531626462643662666266316639306262666539373236
343662313265376261316636623963353933613366353737363435
```
---
```yaml
# vars/vars.yml
---
database_name: "{{ vault_database_name }}"
database_user: "{{ vault_database_user }}"
database_password: "{{ vault_database_password }}"
```
---
```yaml
# provision.yml
---
mysql_databases:
- '{{ database_name }}'
mysql_users:
- name: '{{ database_user }}'
password: '{{ database_password }}'
priv: '{{ database_name }}.*:ALL'
```
---
# `ansible-vault edit vault.yml`
---
# `ansible-playbook` <br>`-i hosts.yml` <br>`deploy.yml`<br>`--ask-vault-pass`
---
# **Basic deployment**
---
```yaml
# deploy.yml
tasks:
- name: Creating project directory
file:
path: /app
state: directory
- name: Uploading application
synchronize:
src: "{{ playbook_dir }}/../"
dest: /app
- name: Installing Composer dependencies
composer:
command: install
working_dir: /app
```
---
# Disadvantages
* Single point of failure
* No ability to roll back
* Sensitive data stored in plain text
---
# **Better deployments**
---
![full inline](images/ansistrano.png)
^ Just another role, specifically for deployments
Ansible port of Capistrano
---
# Features
* Multiple release directories
* Shared paths and files
* Customisable
* Multiple deployment strategies
* Multi-stage environments
* Prune old releases
* Rollbacks
^ rsync, Git, SVN etc
---
```yaml
# requirements.yml
---
# ...
- ansistrano.deploy
- ansistrano.rollback
```
---
```yaml
# deploy.yml
---
- hosts: all
roles:
- ansistrano.deploy
```
---
```yaml
# deploy.yml
---
# ...
vars:
project_deploy_dir: /app
ansistrano_deploy_to: '{{ project_deploy_dir }}'
ansistrano_deploy_via: git
ansistrano_git_branch: master
ansistrano_git_repo: 'git@github.com:opdavies/dransible'
```
---
# `ansible-playbook` <br>`-i hosts.yml` <br>`deploy.yml`
---
```
PLAY [webservers] ******************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [webservers]
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/setup.yml for webservers
TASK [ansistrano.deploy : ANSISTRANO | Ensure deployment base path exists] *********************************************
ok: [webservers]
TASK [ansistrano.deploy : ANSISTRANO | Ensure releases folder exists] **************************************************
ok: [webservers]
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared elements folder exists] *******************************************
ok: [webservers]
TASK [ansistrano.deploy : ANSISTRANO | Ensure shared paths exists] *****************************************************
ok: [webservers] => (item=web/sites/default/files)
```
---
```
TASK [ansistrano.deploy : Update file permissions] *********************************************************************
changed: [webservers]
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/cleanup.yml for webservers
TASK [ansistrano.deploy : ANSISTRANO | Clean up releases] **************************************************************
changed: [webservers]
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
TASK [ansistrano.deploy : include_tasks] *******************************************************************************
included: /Users/opdavies/.ansible/roles/ansistrano.deploy/tasks/anon-stats.yml for webservers
TASK [ansistrano.deploy : ANSISTRANO | Send anonymous stats] ***********************************************************
skipping: [webservers]
PLAY RECAP *************************************************************************************************************
webservers : ok=33 changed=14 unreachable=0 failed=0 skipped=7 rescued=0 ignored=0
```
---
```bash
vagrant@dransible:/app$ ls -l
total 8
lrwxrwxrwx 1 26 Jul 19 00:15 current -> ./releases/20190719001241Z
drwxr-xr-x 5 4096 Jul 22 20:30 releases
drwxr-xr-x 4 4096 Jul 19 00:00 shared
```
---
```
vagrant@dransible:/app/releases$ ls -l
total 20
drwxr-xr-x 5 4096 Jul 22 20:30 .
drwxr-xr-x 4 4096 Jul 19 00:15 ..
drwxr-xr-x 10 4096 Jul 19 00:02 20190719000013Z
drwxr-xr-x 10 4096 Jul 19 00:14 20190719001241Z
drwxr-xr-x 9 4096 Jul 22 20:30 20190722203038Z
```
---
# `ansible-playbook` <br>`-i hosts.yml` <br>`rollback.yml`
---
```yaml
# rollback.yml
---
- hosts: all
roles:
- ansistrano.rollback
vars:
ansistrano_deploy_to: '{{ project_deploy_dir }}'
```
---
# **Customising Ansistrano: <br>Build Hooks**
---
![inline 140%](images/ansistrano-flow.png)
^ Shared = files directory, logs
Before/after symlink shared = run tests
Symlink = 'current' symlink, site is live
Clean up = remove node_modules, database export, sqlite testing DB
---
```yaml
# deploy.yml
---
# ...
ansistrano_after_symlink_shared_tasks_file: '{{ playbook_dir }}/deploy/after-symlink-shared.yml'
ansistrano_after_symlink_tasks_file: '{{ playbook_dir }}/deploy/after-symlink.yml'
ansistrano_after_update_code_tasks_file: '{{ playbook_dir }}/deploy/after-update-code.yml'
release_web_path: '{{ ansistrano_release_path.stdout }}/web'
release_drush_path: '{{ ansistrano_release_path.stdout }}/vendor/bin/drush'
```
^ Each step has a 'before' and 'after' step
Ansistrano allows us to add more things by providing a path to a playbook and adding additional steps.
---
```yaml
# deploy/after-update-code.yml
---
- name: Install Composer dependencies
composer:
command: install
working_dir: '{{ ansistrano_release_path.stdout }}'
```
---
```yaml
# deploy/after-symlink-shared.yml
---
- name: Run database updates
command: '{{ release_drush_path }} --root {{ release_web_path }} updatedb'
```
---
```yaml
# deploy/after-symlink.yml
---
- name: Clear Drupal cache
command: '{{ release_drush_path }} --root {{ release_web_path }} cache-rebuild'
```
---
![80%](images/drupalcon/site.png)
---
# **Managing data <br>across deployments**
---
```yaml
# deploy.yml
vars:
# ...
ansistrano_shared_paths:
- "{{ drupal_root }}/sites/default/files"
```
---
```
vagrant@dransible:/app/shared/web/sites/default/files$ ls -la
total 28
drwxrwxrwx 6 4096 Jul 19 00:18 .
drwxr-xr-x 3 4096 Jul 19 00:00 ..
drwxrwxr-x 2 4096 Jul 22 21:24 css
-rwxrwxrwx 1 487 Jul 19 00:02 .htaccess
drwxrwxr-x 2 4096 Jul 19 00:19 js
drwxrwxrwx 3 4096 Jul 19 00:18 php
drwxrwxrwx 2 4096 Jul 19 00:03 styles
```
---
```
vagrant@dransible:/app/current/web/sites/default$ ls -la
total 48
dr-xr-xr-x 2 4096 Jul 19 00:14 .
drwxr-xr-x 3 4096 Jan 22 17:30 ..
-rw-r--r-- 1 6762 Jul 19 00:14 default.services.yml
-rw-r--r-- 1 31342 Jul 19 00:14 default.settings.php
lrwxrwxrwx 1 45 Jul 19 00:14 files -> ../../../../../shared/web/sites/default/files
-rw-r--r-- 1 35 Jul 19 00:12 settings.php
```
---
# **Generating settings <br>files per deployment**
---
```yaml
# vars/vault.yml
---
vault_database_name: main
vault_database_user: user
vault_database_password: secret
vault_hash_salt: dfgiy$fd2!34gsf2*34g74
```
---
```yaml
# vars/vars.yml
---
database_name: "{{ vault_database_name }}"
database_password: "{{ vault_database_password }}"
database_user: "{{ vault_database_user }}"
hash_salt: "{{ vault_hash_salt }}"
```
---
```yaml
# vars/vars.yml
---
drupal_settings:
- drupal_root: /app/web
sites:
- name: default
settings:
databases:
default:
default:
driver: mysql
host: localhost
database: '{{ database_name }}'
username: '{{ database_user }}'
password: '{{ database_password }}'
hash_salt: '{{ hash_salt }}'
config_directories:
sync: ../config/sync
```
---
```php
// templates/settings.php.j2
// {{ ansible_managed }}
{% for key, values in item.1.settings.databases.items() %}
{% for target, values in values.items() %}
$databases['{{ key }}']['{{ target }}'] = array(
'driver' => '{{ values.driver|default('mysql') }}',
'host' => '{{ values.host|default('localhost') }}',
'database' => '{{ values.database }}',
'username' => '{{ values.username }}',
'password' => '{{ values.password }}',
);
{% endfor %}
{% endfor %}
{% if item.1.settings.base_url is defined %}
$base_url = '{{ item.1.settings.base_url }}';
{% endif %}
{# ... #}
```
---
```yaml
# tasks/main.yml
---
- name: Ensure directory exists
file:
state: directory
path: '{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}'
with_subelements:
- '{{ drupal_settings }}'
- sites
no_log: true
- name: Create settings files
template:
src: settings.php.j2
dest: '{{ item.0.drupal_root }}/sites/{{ item.1.name|default("default") }}/{{ item.1.filename|default("settings.php") }}'
with_subelements:
- '{{ drupal_settings }}'
- sites
no_log: true
```
---
# **Multiple environments**
## Dev, test, production
---
```yaml
mysql_databases:
- name: production
- name: staging
mysql_users:
- name: production
password: '{{ live_db_password }}'
priv: '{{ live_db_name }}.*:ALL'
- name: staging
password: '{{ staging_db_password }}'
priv: staging.*:ALL
```
---
```yaml
# inventories/live.yml
---
all:
hosts:
webservers:
ansible_ssh_host: 192.168.33.10
ansible_ssh_port: 22
project_deploy_path: /app
git_branch: master
drupal_hash_salt: "{{ vault_drupal_hash_salt }}"
drupal_install: true
drupal_settings:
# ...
```
---
```yaml
# inventories/staging.yml
---
all:
hosts:
webservers:
ansible_ssh_host: 192.168.33.10
ansible_ssh_port: 22
project_deploy_path: /app-test
git_branch: develop
drupal_hash_salt: "{{ vault_drupal_hash_salt }}"
drupal_install: true
drupal_settings:
# ...
```
---
# `ansible-playbook deploy.yml -i inventories/staging.yml`
---
# `ansible-playbook deploy.yml -i inventories/live.yml`
---
# **Questions?**

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 523 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 417 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 344 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 385 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 420 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

View file

@ -0,0 +1,62 @@
# Having fun with Drupal 8, PHP libraries Drupal.org API
- Open slides
- Open Chrome tabs
- Open PhpStorm for all projects
- Open Sequel Pro for both DBs
+ Clear cache tables
- Start PHP server for each site
- Start recording
## PHP library
- Show in PhpStorm
- Show readme with examples
+ Run test.php
- Show query classes
+ Not drupal coding standards (PSR-2)
+ Explain laravel collections
- Show entity classes
- Show tests
+ Show fake query classes
- Run tests
## Project statistics
- Show /projects page
- Show routing
- Show ProjectController
+ Using PHP 7 return types
+ Explain dependency injection
+ Explain about collection
+ Explain render array
+ Show how to change ordering
- Show ProjectRetriever
+ More dependency injection
+ Show services file
- Show settings form
- Add another module (Sophie's simple integrations?)
+ See it loading on projects page
+ See it cached
## Drupalversary
- Show front page
- Show block
- Show block form
- Show accountretriever
+ Highlight caching
+ Services file
+ Show cached items in the DB
- Show routing
- Show UserController
+ More dependency injection
+ Services file
- Show date parser
+ Show drupalversary model
- Show adding own username via form
+ See it cached
+ Show adding uid via form
+ See it cached
- Show Dries' because this year drupalversary has passed
- Try with attendee ID

View file

@ -0,0 +1,210 @@
autoscale: true
build-lists: true
footer-style: alignment(left)
footer: @opdavies | opdavi.es
header-emphasis: #53B0EB
header: alignment(left)
text: alignment(left)
text-emphasis: #53B0EB
theme: poster, 8
[.header: alignment(center)]
## Having fun with
## _Drupal 8_, _PHP libraries_
## and the _Drupal.org API_
---
[.background-color: #FFFFFF]
[.build-lists: false]
[.header: #111111]
[.text: #111111, alignment(left)]
![right 700%](../images/me-phpnw.png)
- Full stack Web Developer & System Administrator
- Senior Developer at Microserve
- Part-time freelancer
- Acquia certified Drupal 8 Grand Master
- Drupal core contributor
- Open source project maintainer
- opdavies (Drupal.org, GitHub, Twitter)
- www.oliverdavies.uk
^ Work at Microserve.
Maintain Drupal modules, PHP CLI tools and libraries
Talking about some open source project that I've written or in the process of writing.
---
[.header: alignment(center)]
## _Drupal 8 crash course_
### PHP libraries, Unit testing,
### Composer, Routing, Services,
### Dependency injection, caching
^ Please feel free to ask questions as we go along!
---
[.header: alignment(center)]
## _Live Demo_ alert!
---
[.hide-footer]
![fit](images/microserve-website.png)
^ In 2018 we rebuilt the Microserve website
---
[.hide-footer]
![fit](images/spatie-website.png)
^ Looking for a way to replicate Spatie's open source page for Drupal
---
### _Did you know that_
## Drupal.org has an API?
^ We're all familiar with Drupal.org (it's where everyone registered for this event), but did you know D.o has a public API?
---
[.hide-footer]
![fit](images/api-page.png)
---
[.hide-footer]
![fit](images/api-result.png)
---
### _A PHP library for the_
## Drupal.org API
^ The first thing I built was...
It's what the Drupal examples are built on top of.
---
- Retrieve _node_ and _user_ data
- Filter by properties
- Provides own entity classes
- Methods for retrieving common properties
- Reusable
- Unit tested
^ Re-usable by Drupal 7, Drupal 8, Symfony, Laravel etc
Testing already covered.
Demo
---
[.hide-footer]
![fit](images/library-github.png)
---
[.hide-footer]
![fit](images/library-packagist.png)
---
[.hide-footer]
![fit](images/library-travis.png)
---
```bash
$ composer require opdavies/drupalorg-api-php
```
---
### _Displaying Drupal.org_
## project statistics
---
[.text: alignment(left)]
- Inspired by _spatie.be/en/opensource_
- Displays _downloads_ and _stars_
- Drupal 8 module
+ Configuration form to enter project IDs
+ Queries the API
+ Displays project information
+ Retrieved data cached locally
^ Demo
---
[.hide-footer]
![fit](images/do-projects-settings.png)
---
[.hide-footer]
![fit](images/do-projects-page.png)
---
### _When is your_
## Drupalversary?
---
[.text: alignment(left)]
- When did you register on Drupal.org?
- Drupal 8 module
+ Enter a username
+ Queries the API
+ Displays information - next Drupalversary _date_ and _number of days_ until, _number of years_ on Drupal.org
+ Retrieved data cached locally
^ Stored in cache tables, but could use a custom entity?
Demo
---
[.hide-footer]
![fit](images/drupalversary.png)
---
- opdavi.es/_talks_
- opdavi.es/_do-library_
- opdavi.es/_drupalversary_
- opdavi.es/_do-projects_
---
[.header: alignment(center)]
## Questions?
---
[.header: alignment(center)]
# Thanks

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
src/images/me-phpnw.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

BIN
src/images/me-precedent.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

View file

@ -0,0 +1,10 @@
<svg class="w-10 h-10 lg:w-12 lg:h-12 block" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg">
<title>Tailwind CSS</title>
<path d="M13.5 11.1C15.3 3.9 19.8.3 27 .3c10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05zM0 27.3c1.8-7.2 6.3-10.8 13.5-10.8 10.8 0 12.15 8.1 17.55 9.45 3.6.9 6.75-.45 9.45-4.05-1.8 7.2-6.3 10.8-13.5 10.8-10.8 0-12.15-8.1-17.55-9.45-3.6-.9-6.75.45-9.45 4.05z" transform="translate(5 16)" fill="url(#logoMarkGradient)" fill-rule="evenodd"></path>
<defs>
<linearGradient x1="0%" y1="0%" y2="100%" id="logoMarkGradient">
<stop stop-color="#2298BD"></stop>
<stop offset="1" stop-color="#0ED7B5"></stop>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 918 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -0,0 +1,883 @@
autoscale: true
theme: Plain Jane, 1
# **Taking Flight with <br>Tailwind CSS**
![40%](images/tailwind.svg)
^ Tailwind CSS is a framework that I've been using for the last year and a half
Going to be using Tailwind 1.0 which was released recently (May 13th)
---
- PHP and Front End Developer
- System Administrator
- Senior Engineer at Inviqa
- Part-time freelancer
- Open sourcer
- @opdavies
- oliverdavies.uk
![right](../images/me-phpnw.png)
^ Co-organiser of PHP South Wales and DrupalCamp Bristol
---
![125%](images/techs.png)
---
# **What is Tailwind CSS?**
---
[.footer: tailwindcss.com]
# A **utility-first** CSS framework for rapidly building **custom designs**.
^ CSS utility class generator
PostCSS
Make different looking sites using the same class names
No "Tailwind looking site" like there is with Bootstrap
---
[.footer: tailwindcss.com]
# Tailwind CSS is a **highly customizable**, **low-level** CSS framework
^ No components like Bootstrap or Bulma
Configure it per project
Extendable if needed via additional plugins
Avoids the need to name things prematurely
Can extract components if needed (reusability)
---
[.footer: tailwindcss.com/docs/what-is-tailwind/#designed-to-be-customized]
# Tailwind is more than a CSS framework, it's an engine for <br>**creating design systems**.
^ Good default values provided - colours, fonts, padding, widths
Designing with constraints. Using inline styles, every value is a magic number. With utilities, you're choosing styles from a predefined design system, which makes it much easier to build visually consistent UIs.
---
- Text/border/background colours
- Font size/family/weight
- Alignment
- Padding/margin/negative margin
- Flexbox
- Positioning
- Lists
- z-index
- Opacity
- ...
^ All generated from a single, customisable configuration file.
---
![](images/screenshot-laravel-nova.png)
---
![](images/screenshot-send-firefox.png)
---
![](images/screenshot-rebuilding-bartik.png)
---
# **How do I use Tailwind?**
^ From the new tailwindcss.com website
---
# With Tailwind, you style elements <br>by **applying pre-existing classes** directly in your HTML.
---
# Using **utility classes** to build custom designs **without writing CSS**
---
## **Benefits**
- You aren't wasting time and energy inventing class names
- Your CSS stops growing
- Making changes feels safer
^ No more adding silly class names like sidebar-inner-wrapper just to be able to style something, and no more agonizing over the perfect abstract name for something that's really just a flex container.
^ Using a traditional approach, your CSS files get bigger every time you add a new feature. With utilities, everything is reusable so you rarely need to write new CSS.
^ CSS is global and you never know what you're breaking when you make a change. Classes in your HTML are local, so you can change them without worrying about something else breaking.
---
![500%](images/example/0.png)
---
![500%](images/example/1.png)
^ Add padding with p-6
---
![500%](images/example/2.png)
^ Rounded image - rounded-full
---
![500%](images/example/3.png)
^ Centre image using mx-auto
---
![500%](images/example/4.png)
^ Larger text - text-lg
---
![500%](images/example/5.png)
^ Purple text - text-purple-500
---
![500%](images/example/6.png)
^ Grey text - text-gray-600
---
![500%](images/example/7.png)
^ Centre text - text-center
---
![500%](images/example/8.png)
^ Responsive: enable flexbox on medium screens - md:flex
---
![500%](images/example/9.png)
^ Remove margin around image - md:mx-0
---
![500%](images/example/10.png)
^ Re-align text on medium screens - md:text-left
---
![500%](images/example/11.png)
^ md:mr-6 - add margin to the side of the image on medium screens
---
![500%](images/example/12.png)
^ Increase image size - md:h-24 md:w-24
---
![500%](images/example/13.png)
^ Smaller view
---
# **How do I install Tailwind?**
---
# **1. Use the CDN**
---
[.footer: https://next.tailwindcss.com/docs/installation]
## **https://unpkg.com/tailwindcss/dist/tailwind.min.css**
---
[.footer: https://next.tailwindcss.com/docs/installation]
## **To get the most out of Tailwind, <br>you really should install it via npm.**
^ - You can't customize Tailwind's default theme
- You can't use any directives like *@apply*, *@variants*, etc.
- You can't enable features like *group-hover*
- You can't install third-party plugins
---
## **2. Installing Tailwind via NPM**
---
## `npm install --save-dev` <br>`tailwindcss`
## `yarn add -D tailwindcss`
^ Adds it as a dependency to your package.json file
---
## **Adding Tailwind to your CSS**
---
[.code-highlight: 2-7]
```css
# src/css/style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
---
[.code-highlight: 5,9,13]
```
# app.css
@tailwind base;
# Custom base styles
@tailwind components;
# Custom components
@tailwind utilities;
# Custom utilities
```
---
## **Processing your CSS with Tailwind <br>with the build command**
^ Compile the generated CSS
Pass through PostCSS and Tailwind
---
# `npx tailwind build` <br>`src/css/app.css` <br>`-o dist/css/app.css`
---
```css
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.text-justify {
text-align: justify;
}
```
---
## **Processing your CSS with Tailwind <br>with Laravel Mix**
---
# `npm install --save-dev laravel-mix`
---
```js
const mix = require('laravel-mix')
mix.postCss('src/css/app.css', 'dist/css', [
require('tailwindcss')()
])
```
^ PostCSS - useful if you're including other PostCSS plugins like PostCSS Nested
---
```js
const mix = require('laravel-mix')
require('laravel-mix-tailwind')
mix.postCss('src/css/app.css', 'dist/css')
.tailwind()
```
---
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My new website</title>
<link rel="stylesheet" href="/dist/css/app.css">
</head>
<body>
</body>
</html>
```
---
# `npm run dev`
# `npm run watch`
# `npm run prod`
---
# **Interaction states**
## hover, focus, group-hover, focus-within
^ Start to differ from inline styles
---
# `.[state][separator][class]`
^ State = hover, focus, group focus, focus within
Separator = configurable, colon by default
Class = the same utility class that you would have used normally
---
# `.hover:text-red-500`
---
```html
<a href="#" class="text-red-500 hover:text-red-800">
Read more
</a>
```
---
```css
.text-red-500 {
color: #f56565;
}
.hover\:text-red-500:hover {
color: #f56565;
}
.focus\:text-red-500:focus {
color: #f56565;
}
```
---
```js
// defaultConfig.stub.js
variants: {
alignContent: ['responsive'],
alignItems: ['responsive'],
alignSelf: ['responsive'],
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColor: ['responsive', 'hover', 'focus'],
backgroundPosition: ['responsive'],
backgroundRepeat: ['responsive'],
...
```
---
# **Responsive**
^ Mobile first
---
# [fit] `.[screen][separator][class]`
---
```js
// defaultConfig.stub.js
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
},
```
---
# `md:flex`
---
```html
<div class="block md:flex">
<div class="w-full md:w-1/2">
Column 1
</div>
<div class="w-full md:w-1/2">
Column 2
</div>
</div>
```
---
```css
.block {
display: block;
}
@media (min-width: 640px) {
.sm\:block {
display: block;
}
}
@media (min-width: 768px) {
.md\:block {
display: block;
}
}
```
---
# **Keeping Things Small: <br>Controlling the File Size**
---
# Disabling unused variants <br>and core plugins
---
```js
// tailwind.config.js
variants: {
alignContent: ['responsive'],
alignItems: ['responsive'],
alignSelf: ['responsive'],
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
backgroundColor: ['responsive', 'hover', 'focus'],
```
---
```diff
// tailwind.config.js
variants: {
alignContent: ['responsive'],
alignItems: ['responsive'],
- alignSelf: ['responsive'],
+ alignSelf: false,
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
- backgroundColor: ['responsive', 'hover', 'focus'],
+ backgroundColor: ['responsive'],
```
---
# Manually removing unused or unwanted classes
---
```js
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
xl: '1280px',
},
colors: {
transparent: 'transparent',
black: '#000',
white: '#fff',
gray: {
100: '#f7fafc',
200: '#edf2f7',
300: '#e2e8f0',
400: '#cbd5e0',
500: '#a0aec0',
600: '#718096',
700: '#4a5568',
800: '#2d3748',
900: '#1a202c',
},
```
---
```diff
screens: {
sm: '640px',
md: '768px',
lg: '1024px',
- xl: '1280px',
},
colors: {
transparent: 'transparent',
black: '#000',
white: '#fff',
gray: {
100: '#f7fafc',
- 200: '#edf2f7',
300: '#e2e8f0',
- 400: '#cbd5e0',
- 500: '#a0aec0',
600: '#718096',
700: '#4a5568',
- 800: '#2d3748',
900: '#1a202c',
},
```
^ Needs to be done manually
---
# Automatically removing <br>unused classes
---
# `npm install --save-dev laravel-mix-purgecss`
---
```js
const mix = require('laravel-mix')
mix.postCss('src/css/site.css', 'dist/css')
.purgeCss({
folders: ['templates'],
extensions: ['html', 'php', 'twig']
})
```
---
[.code-highlight: 1,3,6-9]
```js
const mix = require('laravel-mix')
require('laravel-mix-purgecss')
mix.postCss('src/css/site.css', 'dist/css')
.purgeCss({
folders: ['templates'],
extensions: ['html', 'php', 'twig']
})
```
^ Can be tricky using Drupal/WordPress as you don't know where the classes could be coming from, no generated output directory
---
# **Avoiding Repetition: <br>Extracting Components**
---
# Does something **justify** <br>becoming a component?
---
# Could the duplication <br>**be moved elsewhere**?
^ Twig partials
Vue components
WordPress template parts
---
```twig
{# base.html.twig #}
{% for item in navItems %}
<a
class="block py-3 px-4 text-sm text-gray-800"
href="{{ item.url }}"
>
{{ item.title }}
</a>
{% endfor %}
```
^ Using a loop
---
```twig
{# classes.html.twig #}
<h2>Adults</h2>
{% include 'class-list' with {
classes: page.classes,
type: 'adults',
} %}
<h2>Kids</h2>
{% include 'class-list' with {
classes: page.classes,
type: 'kids',
} %}
```
^ Move the duplicate markup into a partial, so there's only one version
Pass data in.
---
```css
a.btn {
@apply text-sm no-underline font-bold;
@apply rounded-full inline-block px-5 py-2;
@apply text-white bg-blue-600;
}
a.btn:hover {
@apply bg-blue-700;
}
```
^ Use utilities as mixins
Copy classes from markup
Still re-using the same design system and constraints as before
---
```css
a.btn {
font-size: 0.875rem;
text-decoration: none;
font-weight: 700;
border-radius: 9999px;
display: inline-block;
padding-left: 1.25rem;
padding-right: 1.25rem;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
color: #fff;
background-color: #3182ce;
}
a.btn:hover {
background-color: #2b6cb0;
}
```
---
# **Customising Tailwind**
---
# `npx tailwind init`
---
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {}
},
plugins: [],
variants: {}
}
```
---
[.code-highlight: 5-7]
```js
// tailwind.config.js
module.exports = {
theme: {
colors: {
inherit: 'inherit'
},
extend: {}
},
plugins: [],
variants: {}
}
```
^ Overrides all colours.
---
[.code-highlight: 5-9]
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
inherit: 'inherit'
}
}
},
plugins: [],
variants: {}
}
```
^ Extends Tailwind's default colours
---
[.code-highlight: 1,4-5]
```js
// tailwind.config.js
module.exports = {
prefix: '',
important: false,
theme: {
extend: {}
},
plugins: [],
variants: {}
}
```
---
# `npx tailwind init --full`
---
# **Extending Tailwind CSS <br>with Plugins**
---
# `npm install --save-dev tailwindcss-list-reset`
---
[.code-highlight: 7-9]
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {}
},
plugins: [
require('tailwindcss-list-reset')()
],
variants: {}
}
```
---
```css
.list-reset {
list-style: none;
padding: 0;
}
```
---
```js
// index.js
module.exports = (variants) => ({ addUtilities }) => {
addUtilities({
'.list-reset': {
listStyle: 'none',
padding: 0
}
}, variants)
}
```
---
# **Demo**
---
## **Resources**
- tailwindcss.com
- tailwindcomponents.com
- builtwithtailwind.com
- github.com/aniftyco/awesome-tailwind
- youtube.com/adamwathan
- opdavi.es/tailwind-repos
- opdavi.es/tags/tailwind-css
---
# **Questions?**
---
# **Thanks!**
# opdavi.es/talks/tailwind
## _@opdavies_ <br>_oliverdavies.uk_
^ Find this talk at opdavi.es/talks/tailwind
Follow me on Twitter
oliverdavies.uk where I blog about PHP, Drupal, Symfony, automated testing, Tailwind etc.
Subscribe to the RSS feed

Some files were not shown because too many files have changed in this diff Show more