diff --git a/.dockerignore b/.dockerignore index a29d127f..b076585c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,14 +1,15 @@ .env -.env.example -.env.testing +.env.* .git .gitattributes .gitignore .gitlab-ci.yml .phpstorm.meta.php .release.json +.server/ssl .travis.yml _ide_helper.php +ansible backups build codeception.yml diff --git a/.env.example b/.env.example index f9e7bd07..08570a6b 100644 --- a/.env.example +++ b/.env.example @@ -4,9 +4,6 @@ APP_DEBUG=true APP_LOG_LEVEL=debug APP_URL=http://localhost -SITE_NAME="G-ZERO CMS" -SITE_DESC="Content management system." - DOMAIN=dev.gzero.pl SESSION_DOMAIN=.dev.gzero.pl SESSION_SECURE_COOKIE=false diff --git a/.gitignore b/.gitignore index 0de15fa1..1691543d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ Homestead.yaml /public/gzero/* tests/_output/* .phpstorm.meta.php -_ide_helper.php \ No newline at end of file +_ide_helper.php +.vscode \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9dd547c3..0f621aac 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -23,7 +23,7 @@ test: - postgres:9.6-alpine - redis:3-alpine script: - - composer install --prefer-dist --no-interaction --no-suggest + - composer install --prefer-dist --no-interaction --no-suggest --no-progress - "php vendor/bin/codecept run -c codeception.yml -o \"settings: lint: false\" --env platform" build: @@ -33,12 +33,15 @@ build: services: - docker:1.12.5-dind script: - - composer install --prefer-dist --no-interaction --no-dev --no-suggest + - composer install --prefer-dist --no-interaction --no-dev --no-suggest --no-progress --optimize-autoloader - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN registry.gitlab.com - if [ -n "${CI_BUILD_TAG}" ]; then docker build --pull -t registry.gitlab.com/grupazero/staging:${CI_BUILD_TAG} .; fi - if [ -n "${CI_BUILD_TAG}" ]; then docker push registry.gitlab.com/grupazero/staging:${CI_BUILD_TAG}; fi - if [ -z "${CI_BUILD_TAG}" ]; then docker build --pull -t registry.gitlab.com/grupazero/staging:latest .; fi - if [ -z "${CI_BUILD_TAG}" ]; then docker push registry.gitlab.com/grupazero/staging:latest; fi + only: + - tags + - master deploy to staging: image: williamyeh/ansible:alpine3 @@ -46,20 +49,27 @@ deploy to staging: environment: staging before_script: - eval $(ssh-agent -s) - - echo "$SSH_PRIVATE_KEY" > ssh.key + - echo "$STAGING_SSH_PRIVATE_KEY" > ssh.key - chmod 600 ssh.key - ssh-add ssh.key - mkdir -p ~/.ssh - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' script: - - "ansible-playbook -i ansible/hosts - -e APP_KEY=$CI_APP_KEY + # TODO Validate if all env values are present + - "ansible-playbook -i ansible/staging + -e APP_KEY=$STAGING_APP_KEY -e APP_VERSION=$CI_BUILD_TAG - -e DOMAIN=$CI_DOMAIN - -e POSTGRES_USER=$CI_DB_USER - -e POSTGRES_PASSWORD=$CI_DB_PASSWORD - -e POSTGRES_DATABASE=$CI_DB_DATABASE - ansible/playbooks/deploy-staging.yml" + -e DOMAIN=$STAGING_DOMAIN + -e POSTGRES_USER=$STAGING_DB_USER + -e POSTGRES_PASSWORD=$STAGING_DB_PASSWORD + -e POSTGRES_DATABASE=$STAGING_DB_DATABASE + -e MAIL_USERNAME=$STAGING_MAIL_USERNAME + -e MAIL_PASSWORD=$STAGING_MAIL_PASSWORD + -e S3_KEY=$STAGING_S3_KEY + -e S3_SECRET=$STAGING_S3_SECRET + -e S3_BUCKET=$STAGING_S3_BUCKET + -e LETSENCRYPT_EMAIL=$STAGING_LETSENCRYPT_EMAIL + ansible/deploy-staging.yml" only: - tags - master @@ -70,13 +80,27 @@ deploy to production: environment: production before_script: - eval $(ssh-agent -s) - - echo "$SSH_PRIVATE_KEY" > ssh.key + - echo "$PRODUCTION_SSH_PRIVATE_KEY" > ssh.key - chmod 600 ssh.key - ssh-add ssh.key - mkdir -p ~/.ssh - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' script: - - echo "@TODO Implement" + # TODO Validate if all env values are present + - "ansible-playbook -i ansible/production + -e APP_KEY=$PRODUCTION_APP_KEY + -e APP_VERSION=$CI_BUILD_TAG + -e DOMAIN=$PRODUCTION_DOMAIN + -e POSTGRES_USER=$PRODUCTION_DB_USER + -e POSTGRES_PASSWORD=$PRODUCTION_DB_PASSWORD + -e POSTGRES_DATABASE=$PRODUCTION_DB_DATABASE + -e MAIL_USERNAME=$PRODUCTION_MAIL_USERNAME + -e MAIL_PASSWORD=$PRODUCTION_MAIL_PASSWORD + -e S3_KEY=$PRODUCTION_S3_KEY + -e S3_SECRET=$PRODUCTION_S3_SECRET + -e S3_BUCKET=$PRODUCTION_S3_BUCKET + -e LETSENCRYPT_EMAIL=$PRODUCTION_LETSENCRYPT_EMAIL + ansible/deploy-production.yml" only: - tags - master diff --git a/.server/nginx/site.conf b/.server/nginx/site.conf new file mode 100644 index 00000000..b5bc7c18 --- /dev/null +++ b/.server/nginx/site.conf @@ -0,0 +1,72 @@ +server { + listen 80; + server_name "www.{{DEFAULT_HOST}}"; + return 301 http://{{DEFAULT_HOST}}$request_uri; +} +server { + listen 80; + server_name .{{DEFAULT_HOST}}; + + root /var/www/public; + index index.php; + + # Cache everything for better performance + location ~* \.(jpg|jpeg|png|gif|ico|svg|woff|woff2|css|js)$ { + expires 14d; + } + + # Disable sendfile - https://docs.vagrantup.com/v2/synced-folders/virtualbox.html + sendfile off; + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + # Stdout logging + error_log /dev/stdout info; + access_log /dev/stdout; + + # Remove index.php$ + if ($request_uri ~* "^(.*/)index\.php$") { + return 301 $1; + } + + location / { + try_files $uri $uri/ /index.php?$query_string; + + # Remove from everywhere index.php + if ($request_uri ~* "^(.*/)index\.php(/?)(.*)") { + return 301 $1$3; + } + } + + # Remove trailing slash. + if (!-d $request_filename) { + rewrite ^/(.+)/$ /$1 permanent; + } + + # Clean Double Slashes + if ($request_uri ~* "\/\/") { + rewrite ^/(.*) /$1 permanent; + } + + location ~ \.php$ { + try_files $uri = 404; + fastcgi_split_path_info ^(.+\.php)(/.+)$; + fastcgi_pass unix:/var/run/php7.1-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + # deny access to . files, for security + # + location ~ /\. { + log_not_found off; + deny all; + } +} +server { + listen 80; + server_name _; + rewrite ^ http://{{DEFAULT_HOST}}$request_uri permanent; +} diff --git a/.server/ssl/.gitignore b/.server/ssl/.gitignore new file mode 100755 index 00000000..d6b7ef32 --- /dev/null +++ b/.server/ssl/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/Dockerfile b/Dockerfile index 9a4c2f35..eb5c35f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ -FROM gzero/platform-container:v2 +FROM gzero/platform-container:v3 MAINTAINER Adrian Skierniewski COPY . /var/www/ +COPY ./.server/nginx/site.conf /etc/nginx/conf.d/site.template RUN chown www-data:www-data -R /var/www \ No newline at end of file diff --git a/README.md b/README.md index 937e8250..b972a3ba 100644 --- a/README.md +++ b/README.md @@ -182,4 +182,4 @@ We're providing some boilerplate configs for travis & gitlab-ci so that you can ## Deployment -We're using ansible to as automation tool. We include example deploy-staging.yml playbook. \ No newline at end of file +We're using Ansible as automation tool. We include some example playbooks. \ No newline at end of file diff --git a/ansible/deploy-production.yml b/ansible/deploy-production.yml new file mode 100644 index 00000000..550bd404 --- /dev/null +++ b/ansible/deploy-production.yml @@ -0,0 +1,89 @@ +--- +- hosts: all + remote_user: root + tasks: + - name: Create directories + file: + path: "{{ item.path }}" + state: directory + owner: www-data + group: www-data + mode: 0755 + with_items: + - { path: '/root/backups'} + - { path: '/root/backups/deployment'} + - { path: '/root/production'} + - { path: '/root/production/volumes'} + - { path: '/root/production/volumes/certs'} + - { path: '/root/production/volumes/conf.d'} + - { path: '/root/production/volumes/vhost.d'} + - { path: '/root/production/volumes/templates'} + - name: 'Ensure that database exists' + become: true + become_user: postgres + postgresql_db: + name: "{{ POSTGRES_DATABASE }}" + encoding: UTF-8 + lc_collate: pl_PL.UTF-8 + lc_ctype: pl_PL.UTF-8 + template: template0 + - name: 'Ensure that database user exists' + become: true + become_user: postgres + postgresql_user: + db: "{{ POSTGRES_DATABASE }}" + name: "{{ POSTGRES_USER }}" + password: "{{ POSTGRES_PASSWORD }}" + priv: "ALL" + - name: Backup database + become: true + become_user: postgres + shell: > + pg_dump --clean --if-exists {{ POSTGRES_DATABASE }} | + gzip > {{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + - name: Move backup to backups dir + shell: > + mv /var/lib/postgresql/{{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + /root/backups/deployment/{{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + - name: Copy nginx proxy template file + copy: + src: ./templates/nginx-proxy.tmpl + dest: /root/production/volumes/templates/nginx.tmpl + owner: www-data + group: www-data + mode: 0644 + - name: Copy docker-compose file + template: + src: ../templates/production-compose.yml + dest: /root/production/docker-compose.yml + force: yes + - name: Copy env file + template: + src: ../templates/env.j2 + dest: /root/production/env + force: yes + - name: Stop existing containers + docker_service: + project_src: production + state: absent + remove_images: local + remove_volumes: true + - name: Run docker & docker-compose commands + shell: "{{ item.command }}" + args: + chdir: /root/production + with_items: + - { command: 'docker-compose pull'} + - { command: 'docker-compose up -d --remove-orphans'} + - { command: 'docker volume ls -qf dangling=true | xargs -r docker volume rm'} # Remove unused volumes + - { command: 'docker images -q --no-trunc | xargs -r docker rmi || true'} # Remove unused images + - name: Wait for docker containers + wait_for: + port: 443 + delay: 15 + state: drained + timeout: 300 + - name: Run migrations + shell: docker-compose exec -T web_server php /var/www/artisan migrate --force + args: + chdir: /root/production diff --git a/ansible/deploy-staging.yml b/ansible/deploy-staging.yml new file mode 100644 index 00000000..cfc22099 --- /dev/null +++ b/ansible/deploy-staging.yml @@ -0,0 +1,105 @@ +--- +- hosts: all + remote_user: root + tasks: + - name: Check if local staging.htpasswd file exists + local_action: stat path="./staging.htpasswd" + register: st + - name: Create directories + file: + path: "{{ item.path }}" + state: directory + owner: www-data + group: www-data + mode: 0755 + with_items: + - { path: '/root/backups'} + - { path: '/root/backups/deployment'} + - { path: '/root/staging'} + - { path: '/root/staging/volumes'} + - { path: '/root/staging/volumes/certs'} + - { path: '/root/staging/volumes/conf.d'} + - { path: '/root/staging/volumes/htpasswd'} + - { path: '/root/staging/volumes/vhost.d'} + - { path: '/root/staging/volumes/templates'} + - name: 'Ensure that database exists' + become: true + become_user: postgres + postgresql_db: + name: "{{ POSTGRES_DATABASE }}" + encoding: UTF-8 + lc_collate: pl_PL.UTF-8 + lc_ctype: pl_PL.UTF-8 + template: template0 + - name: 'Ensure that database user exists' + become: true + become_user: postgres + postgresql_user: + db: "{{ POSTGRES_DATABASE }}" + name: "{{ POSTGRES_USER }}" + password: "{{ POSTGRES_PASSWORD }}" + priv: "ALL" + - name: Backup database + become: true + become_user: postgres + shell: > + pg_dump --clean --if-exists {{ POSTGRES_DATABASE }} | + gzip > {{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + - name: Move backup to backups dir + shell: > + mv /var/lib/postgresql/{{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + /root/backups/deployment/{{ POSTGRES_DATABASE }}_$(date +%Y%m%d_%H%M).sql.gz + - name: Copy nginx proxy template file + copy: + src: ./templates/nginx-proxy.tmpl + dest: /root/staging/volumes/templates/nginx.tmpl + owner: www-data + group: www-data + mode: 0644 + - name: Copy nginx htpasswd file + copy: + src: ./staging.htpasswd + dest: "/root/staging/volumes/htpasswd/{{ item.dest }}" + owner: www-data + group: www-data + mode: 0644 + with_items: + - { dest: '{{ DOMAIN }}'} + - { dest: 'api.{{ DOMAIN }}'} + - { dest: 'www.{{ DOMAIN }}'} + when: st.stat.exists + - name: Copy docker-compose file + template: + src: ../templates/staging-compose.yml + dest: /root/staging/docker-compose.yml + force: yes + - name: Copy env file + template: + src: ../templates/env.j2 + dest: /root/staging/env + force: yes + - name: Stop existing containers + docker_service: + project_src: staging + state: absent + remove_images: local + remove_volumes: true + - name: Run docker & docker-compose commands + shell: "{{ item.command }}" + args: + chdir: /root/staging + with_items: + - { command: 'docker-compose pull'} + - { command: 'docker-compose up -d --remove-orphans'} + - { command: 'docker volume ls -qf dangling=true | xargs -r docker volume rm'} # Remove unused volumes + - { command: 'docker images -q --no-trunc | xargs -r docker rmi || true'} # Remove unused images + - name: Wait for docker containers + wait_for: + port: 443 + delay: 15 + state: drained + timeout: 300 + - name: Run migrations + shell: docker-compose exec -T web_server php /var/www/artisan migrate --force + args: + chdir: /root/staging diff --git a/ansible/group_vars/all b/ansible/group_vars/all new file mode 100644 index 00000000..3f37b541 --- /dev/null +++ b/ansible/group_vars/all @@ -0,0 +1,22 @@ +--- +ansible_user: root +ansible_port: 22 +ag_motd_content: | + -------------------------------------------------------------------------- + This system is managed by Ansible + -------------------------------------------------------------------------- + + This is {{ ansible_fqdn }} running {{ ansible_distribution }}. + + NOTE: System and application configuration for this host is managed by + automated systems. To ensure that any changes you make here are not lost, + please contact with your system administrators. + + {% for item in ag_motd_info %} + {% for key, value in item.iteritems() %} + {{ key }}{{ value }} + {% endfor %} + {% endfor %} + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Last ansible run: {{ ansible_date_time.iso8601 }} \ No newline at end of file diff --git a/ansible/hosts b/ansible/hosts deleted file mode 100644 index d60cc545..00000000 --- a/ansible/hosts +++ /dev/null @@ -1,2 +0,0 @@ -[all] -staging.gzero.pl ansible_port=22 \ No newline at end of file diff --git a/ansible/playbooks/deploy-staging.yml b/ansible/playbooks/deploy-staging.yml deleted file mode 100644 index e87b3801..00000000 --- a/ansible/playbooks/deploy-staging.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -- hosts: all - remote_user: root - tasks: - - name: Creates backups directory - file: path=/root/backups state=directory owner=www-data group=www-data mode=0755 - - name: Copy docker-compose file - template: src=../templates/staging-compose.yml dest=/root/docker-compose.yml force=yes - - name: Copy env file - template: src=../templates/env dest=/root/env force=yes - - name: Stop existing containers - shell: docker-compose -p staging stop - - name: Destroy existing containers - shell: docker-compose -p staging rm -f - - name: Pull new containers - shell: docker-compose -p staging pull - - name: Run new containers - shell: docker-compose -p staging up -d - - name: Remove old volumes - shell: docker volume ls -qf dangling=true | xargs -r docker volume rm - - name: Remove old images - shell: docker images -q --no-trunc | xargs -r docker rmi || true - - name: Wait for docker containers - wait_for: - port: 443 - delay: 15 - state: drained - timeout: 300 - - name: Run migrations - shell: docker exec -i staging_web_server_1 php /var/www/artisan migrate --force - - name: Install passport password client - shell: docker exec -i staging_web_server_1 php /var/www/artisan passport:client --password - - name: Install passport password client - shell: docker exec -i staging_web_server_1 php /var/www/artisan passport:client --personal \ No newline at end of file diff --git a/ansible/production b/ansible/production new file mode 100644 index 00000000..ca09356e --- /dev/null +++ b/ansible/production @@ -0,0 +1,2 @@ +[webservers] +web1 ansible_host=app1.grupazero.pl ansible_fqdn=app1.grupazero.pl \ No newline at end of file diff --git a/ansible/provision.yml b/ansible/provision.yml new file mode 100644 index 00000000..b2488abd --- /dev/null +++ b/ansible/provision.yml @@ -0,0 +1,125 @@ +--- +- hosts: all + name: Making sure that server have python2 installed + gather_facts: false + pre_tasks: + - name: install python2 + raw: apt-get update && apt-get install -qq python-simplejson + - name: add pl_PL.UTF-8 locale + shell: locale-gen pl_PL.UTF-8 + +- hosts: all + name: Provisioning server + + roles: + - role: gantsign.oh-my-zsh + users: + - username: root + oh_my_zsh: + theme: gianu + plugins: + - git + tags: [zsh] + - role: angstwad.docker_ubuntu + docker_pkg_name: docker-engine=1.13.1-0~ubuntu-xenial + tags: [docker] + - role: kamaln7.swapfile + swapfile_use_dd: true + swapfile_size: 2048 + swapfile_swappiness: 10 + swapfile_vfs_cache_pressure: 50 + swapfile_location: /swapfile + tags: [swap] + - role: holms.fqdn + fqdn: "{{ ansible_fqdn }}" + hostname: "{{ inventory_hostname_short }}" + when: ansible_fqdn is defined + tags: [fqdn] + - role: adriagalin.motd + tags: [motd] + - role: ANXS.postgresql + postgresql_version: 9.6 + postgresql_locale: 'en_GB.UTF-8' + postgresql_ctype: 'en_GB.UTF-8' + postgresql_default_auth_method: 'md5' + postgresql_listen_addresses: + - "{{ ansible_default_ipv4.address }}" + postgresql_pg_hba_default: + - { type: local, database: all, user: '{{ postgresql_admin_user }}', address: '', method: 'peer', comment: '' } + - { type: local, database: all, user: all, address: '', method: 'md5', comment: '"local" is for Unix domain socket connections only' } + - { type: host, database: all, user: all, address: 'all', method: 'md5', comment: 'IPv4 local connections:' } + tags: [postgres] + + tasks: + - name: 'Install common stuff' + apt: + name: htop + state: present + - name: Disallow password authentication + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^PasswordAuthentication" + line: "PasswordAuthentication no" + state: present + notify: Restart sshd + - name: Disallow root password authentication + lineinfile: + dest: /etc/ssh/sshd_config + regexp: "^PermitRootLogin" + line: "PermitRootLogin without-password" + state: present + notify: Restart sshd + - name: "Setup alternate SSH port to {{ custom_ssh_port }}" + lineinfile: + dest: "/etc/ssh/sshd_config" + regexp: "^Port" + line: "Port {{ custom_ssh_port }}" + state: present + when: custom_ssh_port is defined + notify: Restart sshd + - name: Reset ufw firewall + ufw: + state: reset + - name: Ensure SSH is reloaded if need be + meta: flush_handlers + - name: Set inventory ssh port + set_fact: + ansible_port: "{{ custom_ssh_port }}" + when: custom_ssh_port is defined + - name: Get ssh port from cfg + shell: grep Port /etc/ssh/sshd_config | sed 's/Port\| \| ;//g' + register: ssh_port_grep_result + - name: Set rate limit for ssh port + ufw: + rule: limit + port: "{{ ssh_port_grep_result.stdout }}" + proto: tcp + - name: UFW allow http + ufw: + rule: allow + port: http + proto: tcp + - name: UFW allow https + ufw: + rule: allow + port: https + proto: tcp + - name: UFW allow ssh + ufw: + rule: allow + port: "{{ ssh_port_grep_result.stdout }}" + proto: tcp + - name: UFW allow postgres access from docker containers + ufw: + rule: allow + src: "172.20.0.0/24" + port: 5432 + - name: Enable ufw + ufw: + state: enabled + policy: deny + handlers: + - name: Restart sshd + service: + name: ssh + state: restarted diff --git a/ansible/roles.txt b/ansible/roles.txt new file mode 100644 index 00000000..92ad8fd0 --- /dev/null +++ b/ansible/roles.txt @@ -0,0 +1,6 @@ +kamaln7.swapfile,master +holms.fqdn,1.1 +angstwad.docker_ubuntu,v2.2 +adriagalin.motd,1.1.1 +gantsign.oh-my-zsh,1.2.0 +ANXS.postgresql,v1.7.1 diff --git a/ansible/staging b/ansible/staging new file mode 100644 index 00000000..3a808d81 --- /dev/null +++ b/ansible/staging @@ -0,0 +1,2 @@ +[webservers] +staging1 ansible_host=staging1.grupazero.pl ansible_fqdn=staging1.grupazero.pl \ No newline at end of file diff --git a/ansible/templates/env b/ansible/templates/env.j2 similarity index 71% rename from ansible/templates/env rename to ansible/templates/env.j2 index d3c7e8b8..a2ab435a 100644 --- a/ansible/templates/env +++ b/ansible/templates/env.j2 @@ -2,12 +2,13 @@ APP_ENV=production APP_KEY={{ APP_KEY }} APP_DEBUG=false APP_LOG_LEVEL=error -APP_URL=http://localhost +APP_URL=https://{{ DOMAIN }} DOMAIN={{ DOMAIN }} SESSION_DOMAIN=.{{ DOMAIN }} SESSION_SECURE_COOKIE={{ SSL | default('true') }} +APP_LOCALE={{ APP_LOCALE | default('en') }} MULTILANG_ENABLED={{ MULTILANG_ENABLED | default('true') }} MULTILANG_SUBDOMAIN=false @@ -30,11 +31,11 @@ REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp -MAIL_HOST=mailtrap.io -MAIL_PORT=2525 -MAIL_USERNAME=null -MAIL_PASSWORD=null -MAIL_ENCRYPTION=null +MAIL_HOST={{ MAIL_HOST | default('smtp.mailgun.org') }} +MAIL_PORT={{ MAIL_PORT | default('587') }} +MAIL_USERNAME={{ MAIL_USERNAME }} +MAIL_PASSWORD={{ MAIL_PASSWORD }} +MAIL_ENCRYPTION={{ MAIL_ENCRYPTION | default('tls') }} PUSHER_APP_ID= PUSHER_KEY= @@ -59,10 +60,10 @@ DISQUS_API_SECRET= DISQUS_DOMAIN= DISQUS_ENABLED=false -FILE_SYSTEM=local -S3_KEY= -S3_SECRET= -S3_REGION= -S3_BUCKET= +FILE_SYSTEM=s3 +S3_KEY={{ S3_KEY }} +S3_SECRET={{ S3_SECRET }} +S3_REGION=eu-central-1 +S3_BUCKET={{ S3_BUCKET }} UPLOAD_DIR=uploads \ No newline at end of file diff --git a/ansible/templates/nginx-proxy.tmpl b/ansible/templates/nginx-proxy.tmpl new file mode 100644 index 00000000..f03630d5 --- /dev/null +++ b/ansible/templates/nginx-proxy.tmpl @@ -0,0 +1,279 @@ +{{ $CurrentContainer := where $ "ID" .Docker.CurrentContainerID | first }} + +{{ define "upstream" }} +{{ if .Address }} +{{/* If we got the containers from swarm and this container's port is published to host, use host IP:PORT */}} +{{ if and .Container.Node.ID .Address.HostPort }} +# {{ .Container.Node.Name }}/{{ .Container.Name }} +server {{ .Container.Node.Address.IP }}:{{ .Address.HostPort }}; +{{/* If there is no swarm node or the port is not published on host, use container's IP:PORT */}} +{{ else if .Network }} +# {{ .Container.Name }} +server {{ .Network.IP }}:{{ .Address.Port }}; +{{ end }} +{{ else if .Network }} +# {{ .Container.Name }} +server {{ .Network.IP }} down; +{{ end }} +{{ end }} + +# If we receive X-Forwarded-Proto, pass it through; otherwise, pass along the +# scheme used to connect to this server +map $http_x_forwarded_proto $proxy_x_forwarded_proto { +default $http_x_forwarded_proto; +'' $scheme; +} + +# If we receive X-Forwarded-Port, pass it through; otherwise, pass along the +# server port the client connected to +map $http_x_forwarded_port $proxy_x_forwarded_port { +default $http_x_forwarded_port; +'' $server_port; +} + +# If we receive Upgrade, set Connection to "upgrade"; otherwise, delete any +# Connection header that may have been passed to this server +map $http_upgrade $proxy_connection { +default upgrade; +'' close; +} + +# Set appropriate X-Forwarded-Ssl header +map $scheme $proxy_x_forwarded_ssl { +default off; +https on; +} + +gzip_types text/plain text/css application/javascript application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript; + +log_format vhost '$host $remote_addr - $remote_user [$time_local] ' +'"$request" $status $body_bytes_sent ' +'"$http_referer" "$http_user_agent"'; + +access_log off; + +{{ if (exists "/etc/nginx/proxy.conf") }} +include /etc/nginx/proxy.conf; +{{ else }} +# HTTP 1.1 support +proxy_http_version 1.1; +proxy_buffering off; +proxy_set_header Host $http_host; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Connection $proxy_connection; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $proxy_x_forwarded_proto; +proxy_set_header X-Forwarded-Ssl $proxy_x_forwarded_ssl; +proxy_set_header X-Forwarded-Port $proxy_x_forwarded_port; + +# Mitigate httpoxy attack (see README for details) +proxy_set_header Proxy ""; +{{ end }} + +{{ $enable_ipv6 := eq (or ($.Env.ENABLE_IPV6) "") "true" }} +server { +server_name _; # This is just an invalid value which will never trigger on a real hostname. +listen 80; +{{ if $enable_ipv6 }} +listen [::]:80; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; +return 503; +} + +{{ if (and (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +server { +server_name _; # This is just an invalid value which will never trigger on a real hostname. +listen 443 ssl http2; +{{ if $enable_ipv6 }} +listen [::]:443 ssl http2; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; +return 503; + +ssl_session_tickets off; +ssl_certificate /etc/nginx/certs/default.crt; +ssl_certificate_key /etc/nginx/certs/default.key; +} +{{ end }} + +{{ range $host, $containers := groupByMulti $ "Env.VIRTUAL_HOST" "," }} +{{ $is_regexp := hasPrefix "~" $host }} +{{ $upstream_name := when $is_regexp (sha1 $host) $host }} +# {{ $host }} +upstream {{ $upstream_name }} { +{{ range $container := $containers }} +{{ $addrLen := len $container.Addresses }} + +{{ range $knownNetwork := $CurrentContainer.Networks }} +{{ range $containerNetwork := $container.Networks }} +{{ if eq $knownNetwork.Name $containerNetwork.Name }} +## Can be connect with "{{ $containerNetwork.Name }}" network + +{{/* If only 1 port exposed, use that */}} +{{ if eq $addrLen 1 }} +{{ $address := index $container.Addresses 0 }} +{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} +{{/* If more than one port exposed, use the one matching VIRTUAL_PORT env var, falling back to standard web port 80 */}} +{{ else }} +{{ $port := coalesce $container.Env.VIRTUAL_PORT "80" }} +{{ $address := where $container.Addresses "Port" $port | first }} +{{ template "upstream" (dict "Container" $container "Address" $address "Network" $containerNetwork) }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +{{ end }} +} + +{{ $default_host := or ($.Env.DEFAULT_HOST) "" }} +{{ $default_server := index (dict $host "" $default_host "default_server") $host }} + +{{/* Get the VIRTUAL_PROTO defined by containers w/ the same vhost, falling back to "http" */}} +{{ $proto := or (first (groupByKeys $containers "Env.VIRTUAL_PROTO")) "http" }} + +{{/* Get the HTTPS_METHOD defined by containers w/ the same vhost, falling back to "redirect" */}} +{{ $https_method := or (first (groupByKeys $containers "Env.HTTPS_METHOD")) "redirect" }} + +{{/* Get the first cert name defined by containers w/ the same vhost */}} +{{ $certName := (first (groupByKeys $containers "Env.CERT_NAME")) }} + +{{/* Get the best matching cert by name for the vhost. */}} +{{ $vhostCert := (closest (dir "/etc/nginx/certs") (printf "%s.crt" $host))}} + +{{/* vhostCert is actually a filename so remove any suffixes since they are added later */}} +{{ $vhostCert := trimSuffix ".crt" $vhostCert }} +{{ $vhostCert := trimSuffix ".key" $vhostCert }} + +{{/* Use the cert specified on the container or fallback to the best vhost match */}} +{{ $cert := (coalesce $certName $vhostCert) }} + +{{ $is_https := (and (ne $https_method "nohttps") (ne $cert "") (exists (printf "/etc/nginx/certs/%s.crt" $cert)) (exists (printf "/etc/nginx/certs/%s.key" $cert))) }} + +{{ if $is_https }} + +{{ if eq $https_method "redirect" }} +server { +server_name {{ $host }}; +listen 80 {{ $default_server }}; +{{ if $enable_ipv6 }} +listen [::]:80 {{ $default_server }}; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; +return 301 https://$host$request_uri; +} +{{ end }} + +server { +server_name {{ $host }}; +listen 443 ssl http2 {{ $default_server }}; +{{ if $enable_ipv6 }} +listen [::]:443 ssl http2 {{ $default_server }}; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; + +ssl_protocols TLSv1 TLSv1.1 TLSv1.2; +ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS'; + +ssl_prefer_server_ciphers on; +ssl_session_timeout 5m; +ssl_session_cache shared:SSL:50m; +ssl_session_tickets off; + +ssl_certificate /etc/nginx/certs/{{ (printf "%s.crt" $cert) }}; +ssl_certificate_key /etc/nginx/certs/{{ (printf "%s.key" $cert) }}; + +{{ if (exists (printf "/etc/nginx/certs/%s.dhparam.pem" $cert)) }} +ssl_dhparam {{ printf "/etc/nginx/certs/%s.dhparam.pem" $cert }}; +{{ end }} + +{{ if (ne $https_method "noredirect") }} +add_header Strict-Transport-Security "max-age=31536000"; +{{ end }} + +{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} +include {{ printf "/etc/nginx/vhost.d/%s" $host }}; +{{ else if (exists "/etc/nginx/vhost.d/default") }} +include /etc/nginx/vhost.d/default; +{{ end }} + +location / { +{{ if eq $proto "uwsgi" }} +include uwsgi_params; +uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; +{{ else }} +proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; +{{ end }} +{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} +auth_basic "Restricted {{ $host }}"; +auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; +{{ end }} +{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} +include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; +{{ else if (exists "/etc/nginx/vhost.d/default_location") }} +include /etc/nginx/vhost.d/default_location; +{{ end }} +} +} + +{{ end }} + +{{ if or (not $is_https) (eq $https_method "noredirect") }} + +server { +server_name {{ $host }}; +listen 80 {{ $default_server }}; +{{ if $enable_ipv6 }} +listen [::]:80 {{ $default_server }}; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; + +{{ if (exists (printf "/etc/nginx/vhost.d/%s" $host)) }} +include {{ printf "/etc/nginx/vhost.d/%s" $host }}; +{{ else if (exists "/etc/nginx/vhost.d/default") }} +include /etc/nginx/vhost.d/default; +{{ end }} + +location / { +{{ if eq $proto "uwsgi" }} +include uwsgi_params; +uwsgi_pass {{ trim $proto }}://{{ trim $upstream_name }}; +{{ else }} +proxy_pass {{ trim $proto }}://{{ trim $upstream_name }}; +{{ end }} +{{ if (exists (printf "/etc/nginx/htpasswd/%s" $host)) }} +auth_basic "Restricted {{ $host }}"; +auth_basic_user_file {{ (printf "/etc/nginx/htpasswd/%s" $host) }}; +{{ end }} +{{ if (exists (printf "/etc/nginx/vhost.d/%s_location" $host)) }} +include {{ printf "/etc/nginx/vhost.d/%s_location" $host}}; +{{ else if (exists "/etc/nginx/vhost.d/default_location") }} +include /etc/nginx/vhost.d/default_location; +{{ end }} +} +} + +{{ if (and (not $is_https) (exists "/etc/nginx/certs/default.crt") (exists "/etc/nginx/certs/default.key")) }} +server { +server_name {{ $host }}; +listen 443 ssl http2 {{ $default_server }}; +{{ if $enable_ipv6 }} +listen [::]:443 ssl http2 {{ $default_server }}; +{{ end }} +server_tokens off; +access_log /var/log/nginx/access.log vhost; +return 500; + +ssl_certificate /etc/nginx/certs/default.crt; +ssl_certificate_key /etc/nginx/certs/default.key; +} +{{ end }} + +{{ end }} +{{ end }} \ No newline at end of file diff --git a/ansible/templates/production-compose.yml b/ansible/templates/production-compose.yml new file mode 100644 index 00000000..45d0ec6b --- /dev/null +++ b/ansible/templates/production-compose.yml @@ -0,0 +1,82 @@ +version: '2.1' +services: + proxy_server: + image: nginx:1.11-alpine + container_name: production_proxy_server + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./volumes/conf.d:/etc/nginx/conf.d + - ./volumes/vhost.d:/etc/nginx/vhost.d + - ./volumes/certs:/etc/nginx/certs:ro + - /usr/share/nginx/html + networks: + - gznet + web_server: + image: registry.gitlab.com/grupazero/staging:{{ APP_VERSION | default('latest', true) }} + restart: unless-stopped + environment: + OVERRIDE_UMASK: "022" + XDEBUG: "false" + DEFAULT_HOST: "{{ DOMAIN }}" + VIRTUAL_HOST: "{{ DOMAIN }},api.{{ DOMAIN }},www.{{ DOMAIN }}" + LETSENCRYPT_HOST: "{{ DOMAIN }},api.{{ DOMAIN }},www.{{ DOMAIN }}" + LETSENCRYPT_EMAIL: "{{ LETSENCRYPT_EMAIL }}" + extra_hosts: + - "db_server:{{ ansible_default_ipv4.address }}" # we use host postgres server + volumes: + - ./env:/var/www/.env # Pass all credentials to laravel + networks: + - gznet + redis_server: + image: redis:3-alpine + restart: unless-stopped + volumes: + - redisdata:/data + networks: + - gznet + letsencrypt_service: + image: jrcs/letsencrypt-nginx-proxy-companion:v1.4 + restart: unless-stopped + depends_on: + - proxy_server + - docker_gen_service + volumes_from: + - proxy_server + volumes: + - ./volumes/certs:/etc/nginx/certs:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - gznet + environment: + NGINX_DOCKER_GEN_CONTAINER: production_docker_gen_service + docker_gen_service: + image: jwilder/docker-gen:0.7.3 + container_name: production_docker_gen_service + restart: unless-stopped + depends_on: + - proxy_server + volumes_from: + - proxy_server + volumes: + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./volumes/templates:/etc/docker-gen/templates:ro + networks: + - gznet + entrypoint: /usr/local/bin/docker-gen -notify-sighup production_proxy_server -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf +networks: + gznet: + driver: "bridge" + enable_ipv6: false + ipam: + driver: default + config: + - subnet: 172.20.0.0/24 + gateway: 172.20.0.1 +volumes: + dbdata: + driver: "local" + redisdata: + driver: "local" \ No newline at end of file diff --git a/ansible/templates/staging-compose.yml b/ansible/templates/staging-compose.yml index 16c5163a..a80c0545 100644 --- a/ansible/templates/staging-compose.yml +++ b/ansible/templates/staging-compose.yml @@ -1,41 +1,82 @@ -version: "2" +version: '2.1' services: + proxy_server: + image: nginx:1.11-alpine + container_name: staging_proxy_server + restart: unless-stopped + ports: + - "80:80" + - "443:443" + volumes: + - ./volumes/htpasswd:/etc/nginx/htpasswd:ro + - ./volumes/conf.d:/etc/nginx/conf.d + - ./volumes/vhost.d:/etc/nginx/vhost.d + - ./volumes/certs:/etc/nginx/certs:ro + - /usr/share/nginx/html + networks: + - gznet web_server: image: registry.gitlab.com/grupazero/staging:{{ APP_VERSION | default('latest', true) }} - restart: "always" + restart: unless-stopped environment: OVERRIDE_UMASK: "022" - NGINX_HOST: "{{ DOMAIN | default('staging.gzero.pl') }}" - SSL: "{{ SSL | default('true') }}" + XDEBUG: "false" + DEFAULT_HOST: "{{ DOMAIN }}" + VIRTUAL_HOST: "{{ DOMAIN }},api.{{ DOMAIN }},www.{{ DOMAIN }}" + LETSENCRYPT_HOST: "{{ DOMAIN }},api.{{ DOMAIN }},www.{{ DOMAIN }}" + LETSENCRYPT_EMAIL: "{{ LETSENCRYPT_EMAIL }}" + extra_hosts: + - "db_server:{{ ansible_default_ipv4.address }}" # we use host postgres server volumes: - - ./env:/var/www/.env # We need to pass all credentials - ports: - - "80:80" - - "443:443" + - ./env:/var/www/.env # Pass all credentials to laravel networks: - gznet - db_server: - image: postgres:9.6-alpine - restart: "always" + redis_server: + image: redis:3-alpine + restart: unless-stopped + volumes: + - redisdata:/data networks: - gznet + letsencrypt_service: + image: jrcs/letsencrypt-nginx-proxy-companion:v1.4 + restart: unless-stopped + depends_on: + - proxy_server + - docker_gen_service + volumes_from: + - proxy_server volumes: - - ./backups:/backups - - dbdata:/var/lib/postgresql + - ./volumes/certs:/etc/nginx/certs:rw + - /var/run/docker.sock:/var/run/docker.sock:ro + networks: + - gznet environment: - POSTGRES_PASSWORD: {{ POSTGRES_PASSWORD }} - POSTGRES_USER: {{ POSTGRES_USER }} - POSTGRES_DB: {{ POSTGRES_DATABASE }} - redis_server: - image: redis:3-alpine - restart: "always" + NGINX_DOCKER_GEN_CONTAINER: staging_docker_gen_service + docker_gen_service: + image: jwilder/docker-gen:0.7.3 + container_name: staging_docker_gen_service + restart: unless-stopped + depends_on: + - proxy_server + volumes_from: + - proxy_server volumes: - - redisdata:/data + - /var/run/docker.sock:/tmp/docker.sock:ro + - ./volumes/htpasswd:/etc/nginx/htpasswd:ro + - ./volumes/templates:/etc/docker-gen/templates:ro networks: - gznet + entrypoint: /usr/local/bin/docker-gen -notify-sighup staging_proxy_server -watch -wait 5s:30s /etc/docker-gen/templates/nginx.tmpl /etc/nginx/conf.d/default.conf networks: gznet: driver: "bridge" + enable_ipv6: false + ipam: + driver: default + config: + - subnet: 172.20.0.0/24 + gateway: 172.20.0.1 volumes: dbdata: driver: "local" diff --git a/composer.lock b/composer.lock index 295b35c8..66b21d80 100644 --- a/composer.lock +++ b/composer.lock @@ -264,20 +264,21 @@ }, { "name": "filp/whoops", - "version": "2.1.5", + "version": "2.1.8", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "2abce9d956589122c6443d6265f01cf7e9388e3c" + "reference": "f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/2abce9d956589122c6443d6265f01cf7e9388e3c", - "reference": "2abce9d956589122c6443d6265f01cf7e9388e3c", + "url": "https://api.github.com/repos/filp/whoops/zipball/f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3", + "reference": "f2950be7da8b8d6c4e77821b6c9d486e36cdc4f3", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0" + "php": "^5.5.9 || ^7.0", + "psr/log": "^1.0.1" }, "require-dev": { "mockery/mockery": "0.9.*", @@ -320,7 +321,7 @@ "whoops", "zf2" ], - "time": "2016-12-26T16:13:31+00:00" + "time": "2017-03-07T09:04:45+00:00" }, { "name": "firebase/php-jwt", @@ -408,21 +409,21 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.2.2", + "version": "6.2.3", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60" + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ebf29dee597f02f09f4d5bbecc68230ea9b08f60", - "reference": "ebf29dee597f02f09f4d5bbecc68230ea9b08f60", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006", + "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006", "shasum": "" }, "require": { "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.3.1", + "guzzlehttp/psr7": "^1.4", "php": ">=5.5" }, "require-dev": { @@ -466,7 +467,7 @@ "rest", "web service" ], - "time": "2016-10-08T15:01:37+00:00" + "time": "2017-02-28T22:50:30+00:00" }, { "name": "guzzlehttp/promises", @@ -521,16 +522,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.3.1", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", - "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/0d6c7ca039329247e4f0f8f8f6506810e8248855", + "reference": "0d6c7ca039329247e4f0f8f8f6506810e8248855", "shasum": "" }, "require": { @@ -566,16 +567,23 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Schultze", + "homepage": "https://github.com/Tobion" } ], - "description": "PSR-7 message implementation", + "description": "PSR-7 message implementation that also provides common utility methods", "keywords": [ "http", "message", + "request", + "response", "stream", - "uri" + "uri", + "url" ], - "time": "2016-06-24T23:00:38+00:00" + "time": "2017-02-27T10:51:17+00:00" }, { "name": "gzero/admin", @@ -692,12 +700,12 @@ "source": { "type": "git", "url": "https://github.com/GrupaZero/cms.git", - "reference": "3871ff534b5b1564a987cc7faaf8cd186f74154d" + "reference": "3659aeaf04346f40d1555d5d1f3eee4b3a829e21" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrupaZero/cms/zipball/3871ff534b5b1564a987cc7faaf8cd186f74154d", - "reference": "3871ff534b5b1564a987cc7faaf8cd186f74154d", + "url": "https://api.github.com/repos/GrupaZero/cms/zipball/3659aeaf04346f40d1555d5d1f3eee4b3a829e21", + "reference": "3659aeaf04346f40d1555d5d1f3eee4b3a829e21", "shasum": "" }, "require": { @@ -749,20 +757,20 @@ "framework", "laravel" ], - "time": "2017-02-03T18:40:42+00:00" + "time": "2017-03-13T10:49:04+00:00" }, { "name": "gzero/eloquent-tree", - "version": "v3.0.1", + "version": "v3.1.1", "source": { "type": "git", "url": "https://github.com/AdrianSkierniewski/eloquent-tree.git", - "reference": "5f49469b2f661f7c820d103617b6260e87059b24" + "reference": "d2bc288f3c83b922bfae9d117be019ed91c05ed9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/AdrianSkierniewski/eloquent-tree/zipball/5f49469b2f661f7c820d103617b6260e87059b24", - "reference": "5f49469b2f661f7c820d103617b6260e87059b24", + "url": "https://api.github.com/repos/AdrianSkierniewski/eloquent-tree/zipball/d2bc288f3c83b922bfae9d117be019ed91c05ed9", + "reference": "d2bc288f3c83b922bfae9d117be019ed91c05ed9", "shasum": "" }, "require": { @@ -797,7 +805,7 @@ "eloquent", "laravel" ], - "time": "2016-12-09T13:51:20+00:00" + "time": "2017-02-15T16:24:49+00:00" }, { "name": "jakub-onderka/php-console-color", @@ -1246,16 +1254,16 @@ }, { "name": "league/flysystem", - "version": "1.0.34", + "version": "1.0.35", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299" + "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/469ad53c13ea19a0e54e3e5d70f61227ddcc0299", - "reference": "469ad53c13ea19a0e54e3e5d70f61227ddcc0299", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/dda7f3ab94158a002d9846a97dc18ebfb7acc062", + "reference": "dda7f3ab94158a002d9846a97dc18ebfb7acc062", "shasum": "" }, "require": { @@ -1325,7 +1333,7 @@ "sftp", "storage" ], - "time": "2017-01-30T17:41:17+00:00" + "time": "2017-02-09T11:33:58+00:00" }, { "name": "league/fractal", @@ -1468,16 +1476,16 @@ }, { "name": "monolog/monolog", - "version": "1.22.0", + "version": "1.22.1", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558" + "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bad29cb8d18ab0315e6c477751418a82c850d558", - "reference": "bad29cb8d18ab0315e6c477751418a82c850d558", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/1e044bc4b34e91743943479f1be7a1d5eb93add0", + "reference": "1e044bc4b34e91743943479f1be7a1d5eb93add0", "shasum": "" }, "require": { @@ -1542,7 +1550,7 @@ "logging", "psr-3" ], - "time": "2016-11-26T00:15:39+00:00" + "time": "2017-03-13T07:08:03+00:00" }, { "name": "mtdowling/cron-expression", @@ -1643,16 +1651,16 @@ }, { "name": "nikic/php-parser", - "version": "v3.0.2", + "version": "v3.0.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744" + "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/adf44419c0fc014a0f191db6f89d3e55d4211744", - "reference": "adf44419c0fc014a0f191db6f89d3e55d4211744", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2b9e2f71b722f7c53918ab0c25f7646c2013f17d", + "reference": "2b9e2f71b722f7c53918ab0c25f7646c2013f17d", "shasum": "" }, "require": { @@ -1690,20 +1698,20 @@ "parser", "php" ], - "time": "2016-12-06T11:30:35+00:00" + "time": "2017-03-05T18:23:57+00:00" }, { "name": "paragonie/random_compat", - "version": "v2.0.4", + "version": "v2.0.10", "source": { "type": "git", "url": "https://github.com/paragonie/random_compat.git", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e" + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paragonie/random_compat/zipball/a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", - "reference": "a9b97968bcde1c4de2a5ec6cbd06a0f6c919b46e", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/634bae8e911eefa89c1abfbf1b66da679ac8f54d", + "reference": "634bae8e911eefa89c1abfbf1b66da679ac8f54d", "shasum": "" }, "require": { @@ -1738,7 +1746,7 @@ "pseudorandom", "random" ], - "time": "2016-11-07T23:38:38+00:00" + "time": "2017-03-13T16:27:32+00:00" }, { "name": "phpseclib/phpseclib", @@ -1981,16 +1989,16 @@ }, { "name": "psy/psysh", - "version": "v0.8.1", + "version": "v0.8.2", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a" + "reference": "97113db4107a4126bef933b60fea6dbc9f615d41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", - "reference": "701e8a1cc426ee170f1296f5d9f6b8a26ad25c4a", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/97113db4107a4126bef933b60fea6dbc9f615d41", + "reference": "97113db4107a4126bef933b60fea6dbc9f615d41", "shasum": "" }, "require": { @@ -2050,7 +2058,7 @@ "interactive", "shell" ], - "time": "2017-01-15T17:54:13+00:00" + "time": "2017-03-01T00:13:29+00:00" }, { "name": "ramsey/uuid", @@ -2187,16 +2195,16 @@ }, { "name": "swiftmailer/swiftmailer", - "version": "v5.4.5", + "version": "v5.4.6", "source": { "type": "git", "url": "https://github.com/swiftmailer/swiftmailer.git", - "reference": "cd142238a339459b10da3d8234220963f392540c" + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/cd142238a339459b10da3d8234220963f392540c", - "reference": "cd142238a339459b10da3d8234220963f392540c", + "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", + "reference": "81fdccfaf8bdc5d5d7a1ef6bb3a61bbb1a6c4a3e", "shasum": "" }, "require": { @@ -2237,7 +2245,7 @@ "mail", "mailer" ], - "time": "2016-12-29T10:02:40+00:00" + "time": "2017-02-13T07:52:53+00:00" }, { "name": "symfony/console", @@ -2359,16 +2367,16 @@ }, { "name": "symfony/event-dispatcher", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6" + "reference": "b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/9137eb3a3328e413212826d63eeeb0217836e2b6", - "reference": "9137eb3a3328e413212826d63eeeb0217836e2b6", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d", + "reference": "b7a1b9e0a0f623ce43b4c8d775eb138f190c9d8d", "shasum": "" }, "require": { @@ -2415,7 +2423,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-21T09:12:04+00:00" }, { "name": "symfony/finder", @@ -3411,16 +3419,16 @@ }, { "name": "codeception/codeception", - "version": "2.2.8", + "version": "2.2.9", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "0a87d4b19070a24636125993450a9d3f698a6663" + "reference": "0204f1362040d3e408404af2545e5fa27e8964e2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/0a87d4b19070a24636125993450a9d3f698a6663", - "reference": "0a87d4b19070a24636125993450a9d3f698a6663", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/0204f1362040d3e408404af2545e5fa27e8964e2", + "reference": "0204f1362040d3e408404af2545e5fa27e8964e2", "shasum": "" }, "require": { @@ -3431,7 +3439,7 @@ "guzzlehttp/guzzle": ">=4.1.4 <7.0", "guzzlehttp/psr7": "~1.0", "php": ">=5.4.0 <8.0", - "phpunit/php-code-coverage": ">=2.1.3 <5.0", + "phpunit/php-code-coverage": ">=2.2.4 <5.0", "phpunit/phpunit": ">4.8.20 <6.0", "sebastian/comparator": "~1.1", "sebastian/diff": "^1.4", @@ -3499,7 +3507,7 @@ "functional testing", "unit testing" ], - "time": "2017-01-20T00:54:15+00:00" + "time": "2017-02-04T02:04:21+00:00" }, { "name": "doctrine/instantiator", @@ -3788,16 +3796,16 @@ }, { "name": "mockery/mockery", - "version": "0.9.7", + "version": "0.9.9", "source": { "type": "git", "url": "https://github.com/padraic/mockery.git", - "reference": "4de7969f4664da3cef1ccd83866c9f59378c3371" + "reference": "6fdb61243844dc924071d3404bb23994ea0b6856" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/4de7969f4664da3cef1ccd83866c9f59378c3371", - "reference": "4de7969f4664da3cef1ccd83866c9f59378c3371", + "url": "https://api.github.com/repos/padraic/mockery/zipball/6fdb61243844dc924071d3404bb23994ea0b6856", + "reference": "6fdb61243844dc924071d3404bb23994ea0b6856", "shasum": "" }, "require": { @@ -3849,7 +3857,7 @@ "test double", "testing" ], - "time": "2016-12-19T14:50:55+00:00" + "time": "2017-02-28T12:52:32+00:00" }, { "name": "myclabs/deep-copy", @@ -4041,27 +4049,27 @@ }, { "name": "phpspec/prophecy", - "version": "v1.6.2", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb" + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/6c52c2722f8460122f96f86346600e1077ce22cb", - "reference": "6c52c2722f8460122f96f86346600e1077ce22cb", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", + "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", - "sebastian/comparator": "^1.1", - "sebastian/recursion-context": "^1.0|^2.0" + "sebastian/comparator": "^1.1|^2.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { - "phpspec/phpspec": "^2.0", + "phpspec/phpspec": "^2.5|^3.2", "phpunit/phpunit": "^4.8 || ^5.6.5" }, "type": "library", @@ -4100,39 +4108,39 @@ "spy", "stub" ], - "time": "2016-11-21T14:58:47+00:00" + "time": "2017-03-02T20:05:34+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "4.0.5", + "version": "4.0.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971" + "reference": "09e2277d14ea467e5a984010f501343ef29ffc69" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", - "reference": "c19cfc7cbb0e9338d8c469c7eedecc2a428b0971", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/09e2277d14ea467e5a984010f501343ef29ffc69", + "reference": "09e2277d14ea467e5a984010f501343ef29ffc69", "shasum": "" }, "require": { + "ext-dom": "*", + "ext-xmlwriter": "*", "php": "^5.6 || ^7.0", - "phpunit/php-file-iterator": "~1.3", - "phpunit/php-text-template": "~1.2", - "phpunit/php-token-stream": "^1.4.2", - "sebastian/code-unit-reverse-lookup": "~1.0", + "phpunit/php-file-iterator": "^1.3", + "phpunit/php-text-template": "^1.2", + "phpunit/php-token-stream": "^1.4.2 || ^2.0", + "sebastian/code-unit-reverse-lookup": "^1.0", "sebastian/environment": "^1.3.2 || ^2.0", - "sebastian/version": "~1.0|~2.0" + "sebastian/version": "^1.0 || ^2.0" }, "require-dev": { - "ext-xdebug": ">=2.1.4", - "phpunit/phpunit": "^5.4" + "ext-xdebug": "^2.1.4", + "phpunit/phpunit": "^5.7" }, "suggest": { - "ext-dom": "*", - "ext-xdebug": ">=2.4.0", - "ext-xmlwriter": "*" + "ext-xdebug": "^2.5.1" }, "type": "library", "extra": { @@ -4163,7 +4171,7 @@ "testing", "xunit" ], - "time": "2017-01-20T15:06:43+00:00" + "time": "2017-03-01T09:12:17+00:00" }, { "name": "phpunit/php-file-iterator", @@ -4255,25 +4263,30 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.8", + "version": "1.0.9", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", - "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", + "reference": "3dcf38ca72b158baf0bc245e9184d3fdffa9c46f", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4|~5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, "autoload": { "classmap": [ "src/" @@ -4295,20 +4308,20 @@ "keywords": [ "timer" ], - "time": "2016-05-12T18:03:57+00:00" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.9", + "version": "1.4.11", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b" + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/3b402f65a4cc90abf6e1104e388b896ce209631b", - "reference": "3b402f65a4cc90abf6e1104e388b896ce209631b", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", + "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", "shasum": "" }, "require": { @@ -4344,20 +4357,20 @@ "keywords": [ "tokenizer" ], - "time": "2016-11-15T14:06:22+00:00" + "time": "2017-02-27T10:12:30+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.9", + "version": "5.7.15", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd" + "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/69f832b87c731d5cacad7f91948778fe98335fdd", - "reference": "69f832b87c731d5cacad7f91948778fe98335fdd", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b99112aecc01f62acf3d81a3f59646700a1849e5", + "reference": "b99112aecc01f62acf3d81a3f59646700a1849e5", "shasum": "" }, "require": { @@ -4374,14 +4387,14 @@ "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.2.2", + "sebastian/comparator": "^1.2.4", "sebastian/diff": "~1.2", "sebastian/environment": "^1.3.4 || ^2.0", "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.0 || ^2.0", + "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", + "sebastian/version": "~1.0.3|~2.0", "symfony/yaml": "~2.1|~3.0" }, "conflict": { @@ -4426,7 +4439,7 @@ "testing", "xunit" ], - "time": "2017-01-28T06:14:33+00:00" + "time": "2017-03-02T15:22:43+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -4493,24 +4506,24 @@ "source": { "type": "git", "url": "https://github.com/satooshi/php-coveralls.git", - "reference": "50c60bb64054974f8ed7540ae6e75fd7981a5fd3" + "reference": "d5124af8bd6464144d550906cce46bd0df78f428" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/50c60bb64054974f8ed7540ae6e75fd7981a5fd3", - "reference": "50c60bb64054974f8ed7540ae6e75fd7981a5fd3", + "url": "https://api.github.com/repos/satooshi/php-coveralls/zipball/d5124af8bd6464144d550906cce46bd0df78f428", + "reference": "d5124af8bd6464144d550906cce46bd0df78f428", "shasum": "" }, "require": { "ext-json": "*", "ext-simplexml": "*", "guzzlehttp/guzzle": "^6.0", - "php": ">=5.5", + "php": "^5.5 || ^7.0", "psr/log": "^1.0", - "symfony/config": "^2.1|^3.0", - "symfony/console": "^2.1|^3.0", - "symfony/stopwatch": "^2.0|^3.0", - "symfony/yaml": "^2.0|^3.0" + "symfony/config": "^2.1 || ^3.0", + "symfony/console": "^2.1 || ^3.0", + "symfony/stopwatch": "^2.0 || ^3.0", + "symfony/yaml": "^2.0 || ^3.0" }, "suggest": { "symfony/http-kernel": "Allows Symfony integration" @@ -4548,27 +4561,27 @@ "github", "test" ], - "time": "2016-01-20T17:44:41+00:00" + "time": "2017-02-25T12:14:18+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", - "version": "1.0.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe" + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/c36f5e7cfce482fde5bf8d10d41a53591e0198fe", - "reference": "c36f5e7cfce482fde5bf8d10d41a53591e0198fe", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", + "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18", "shasum": "" }, "require": { - "php": ">=5.6" + "php": "^5.6 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "^5.7 || ^6.0" }, "type": "library", "extra": { @@ -4593,7 +4606,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2016-02-13T06:45:14+00:00" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -4881,16 +4894,16 @@ }, { "name": "sebastian/object-enumerator", - "version": "2.0.0", + "version": "2.0.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35" + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", - "reference": "96f8a3f257b69e8128ad74d3a7fd464bcbaa3b35", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7", + "reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7", "shasum": "" }, "require": { @@ -4923,7 +4936,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2016-11-19T07:35:10+00:00" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -5065,16 +5078,16 @@ }, { "name": "symfony/browser-kit", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "548f8230bad9f77463b20b15993a008f03e96db5" + "reference": "2fe0caa60c1a1dfeefd0425741182687a9b382b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/548f8230bad9f77463b20b15993a008f03e96db5", - "reference": "548f8230bad9f77463b20b15993a008f03e96db5", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/2fe0caa60c1a1dfeefd0425741182687a9b382b8", + "reference": "2fe0caa60c1a1dfeefd0425741182687a9b382b8", "shasum": "" }, "require": { @@ -5118,20 +5131,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-21T09:12:04+00:00" }, { "name": "symfony/class-loader", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6" + "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/0152f7a47acd564ca62c652975c2b32ac6d613a6", - "reference": "0152f7a47acd564ca62c652975c2b32ac6d613a6", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", + "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", "shasum": "" }, "require": { @@ -5174,20 +5187,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2017-01-10T14:14:38+00:00" + "time": "2017-02-18T17:28:00+00:00" }, { "name": "symfony/config", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "c5ea878b5a7f6a01b9a2f182f905831711b9ff3f" + "reference": "741d6d4cd1414d67d48eb71aba6072b46ba740c2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/c5ea878b5a7f6a01b9a2f182f905831711b9ff3f", - "reference": "c5ea878b5a7f6a01b9a2f182f905831711b9ff3f", + "url": "https://api.github.com/repos/symfony/config/zipball/741d6d4cd1414d67d48eb71aba6072b46ba740c2", + "reference": "741d6d4cd1414d67d48eb71aba6072b46ba740c2", "shasum": "" }, "require": { @@ -5230,7 +5243,7 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-03-01T18:18:25+00:00" }, { "name": "symfony/css-selector", @@ -5343,16 +5356,16 @@ }, { "name": "symfony/filesystem", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4" + "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", - "reference": "a0c6ef2dc78d33b58d91d3a49f49797a184d06f4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/bc0f17bed914df2cceb989972c3b996043c4da4a", + "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a", "shasum": "" }, "require": { @@ -5388,20 +5401,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-01-08T20:47:33+00:00" + "time": "2017-03-06T19:30:27+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "9aa0b51889c01bca474853ef76e9394b02264464" + "reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/9aa0b51889c01bca474853ef76e9394b02264464", - "reference": "9aa0b51889c01bca474853ef76e9394b02264464", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/c5ee0f8650c84b4d36a5f76b3b504233feaabf75", + "reference": "c5ee0f8650c84b4d36a5f76b3b504233feaabf75", "shasum": "" }, "require": { @@ -5437,20 +5450,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2017-01-02T20:32:22+00:00" + "time": "2017-02-18T17:28:00+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.2", + "version": "v3.2.6", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "50eadbd7926e31842893c957eca362b21592a97d" + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/50eadbd7926e31842893c957eca362b21592a97d", - "reference": "50eadbd7926e31842893c957eca362b21592a97d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/093e416ad096355149e265ea2e4cc1f9ee40ab1a", + "reference": "093e416ad096355149e265ea2e4cc1f9ee40ab1a", "shasum": "" }, "require": { @@ -5492,7 +5505,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-01-03T13:51:32+00:00" + "time": "2017-03-07T16:47:02+00:00" }, { "name": "webmozart/assert", diff --git a/develop b/develop index 60ec3d0d..77e9de35 100755 --- a/develop +++ b/develop @@ -10,6 +10,11 @@ fi DOCKER="${SUDO}docker" COMPOSE="${SUDO}docker-compose" +function die () { + echo >&2 "$@" + exit 1 +} + if [ $# -gt 0 ];then if [ "$1" == "test" ]; then @@ -56,6 +61,28 @@ if [ $# -gt 0 ];then ${DOCKER} volume ls -qf dangling=true | xargs -r ${DOCKER} volume rm ${DOCKER} images -q --no-trunc | xargs -r ${DOCKER} rmi || true + elif [ "$1" == "db-dump" ]; then + shift 1 + [ ! -z $1 ] || die "Please specify database name" + ${COMPOSE} exec db_server sh -c "pg_dump -U postgres --clean --if-exists $1 | gzip > /backups/$1_$(date +%Y%m%d_%H%M).sql.gz" + + elif [ "$1" == "db-restore" ]; then + shift 1 + [ ! -z $1 ] || die "Please specify database name" + [ ! -z $2 ] || die "Please specify dump path" + ${COMPOSE} exec db_server sh -c "gunzip -c /$2 | psql -U postgres --single-transaction -a $1" + + elif [ "$1" == "generate-cert" ]; then + shift 1 + echo $1 | grep -E -q '^localhost$|^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$' || die "Not valid domain" + if [ "$2" == "dhparam" ]; then + openssl dhparam -out ./.server/ssl/$1.dhparam.pem 2048 + fi + openssl req -x509 -newkey rsa:4086 \ + -subj "/C=PL/ST=XXXX/L=XXXX/O=GrupaZero/CN=$1" \ + -keyout ./.server/ssl/$1.key \ + -out ./.server/ssl/$1.crt \ + -days 3650 -nodes -sha256 else ${COMPOSE} "$@" fi diff --git a/docker-compose.yml b/docker-compose.yml index 9efcc070..800072ac 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,28 @@ -version: '2' +version: '2.1' services: + proxy_server: + image: jwilder/nginx-proxy:alpine + restart: "no" + ports: + - "80:80" + - "443:443" + volumes: + - ./.server/ssl:/etc/nginx/certs:ro + - /var/run/docker.sock:/tmp/docker.sock:ro + networks: + - gznet web_server: - image: gzero/platform-container:v2 + image: gzero/platform-container:v3 restart: "no" environment: OVERRIDE_UMASK: "002" - NGINX_HOST: "dev.gzero.pl" - SSL: "true" XDEBUG: "true" + DEFAULT_HOST: "dev.gzero.pl" + VIRTUAL_HOST: "dev.gzero.pl,api.dev.gzero.pl,www.dev.gzero.pl" volumes: - .:/var/www networks: - gznet - ports: - - "80:80" - - "443:443" db_server: image: postgres:9.6-alpine restart: "no" diff --git a/ssl/.gitkeep b/ssl/.gitkeep deleted file mode 100644 index e69de29b..00000000