# Walkthrough ## Summary * [Overview](#overview) * [Pipeline definition](#pipeline-definition) * [Pipeline concurrency](#pipeline-concurrency) * [Environment variables](#environment-variables) * [Triggers](#triggers) * [Steps](#steps) * [Fetch and update submodules](#Fetch-and-update-submodules) * [Replace hosts and user variables](#replace-hosts-and-user-variables) * [Build frontend](#build-frontend) * [Check ansible syntax](#check-ansible-syntax) * [Apply ansible playbook](#apply-ansible-playbook) * [Playbook definition](#playbook-definition) * [Delete old spt-items-api](#delete-old-spt-items-api) * [Copy the project](#copy-the-project) * [Copy PHP env file](#copy-php-env-file) * [Get JavaScript chunks name](#get-javascript-chunks-name) * [Get file names from find output](#get-file-from-find-output) * [Copy app.blade.php file](#copy-app-blade-php-file) * [Download and install composer dependencies](#download-and-install-composer-dependencies) ## Overview * The project is split between the frontend and the backend * the backend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [api](../api) that points towards [https://dev.sp-tarkov.com/Rev/spt-items-api.git](https://dev.sp-tarkov.com/Rev/spt-items-api.git) * the frontend is a [submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) located in [frontend](../frontend) that points towards [https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git](https://dev.sp-tarkov.com/shirito/item-finder-website-frontend.git) * There are two Ansible pipelines * A docker pipeline [drone-docker.yml](../drone-docker.yml) * A kubernetes pipeline [drone-kubernetes.yml](../drone-kubernetes.yml) * All ansible playbook files are located in [.ansible](../.ansible) * The documentation is located in [documentation](../documentation) ## Pipeline definition ```yml kind: pipeline type: kubernetes name: default ``` The pipeline is defined either as Docker or Kubernetes depending on [.drone-docker.yml](../../.drone-docker.yml) or [.drone-kubernetes.yml](../../.drone-kubernetes.yml). The name is set as `default`. ## Pipeline Concurrency ```yml concurrency: limit: 1 ``` The pipeline is set to only one build at a time (every subsequent build with be pending). ## Environment variables ```yml environment: SPT_ITEMS_PATH: /var/www/html/aki/spt-items-api ``` Here are the environment variables. They are automatically injected in every step. ## Triggers ```yml trigger: event: - push branch: - master - main - development ``` The pipeline is run on every push only on branches `master`, `main` and `development`. We want to check that every development on `development` branch is correct and deploy automatically when merged in `master`/`main`. ## Steps ### Replace hosts and user variables ```yml - name: replace hosts and user variables image: ubuntu:impish environment: DEPLOY_HOSTNAME: from_secret: deploy_hostname SPT_ITEMS_HOSTNAME: from_secret: spt_items_hostname DEPLOYMENT_USER: from_secret: deploy_username commands: - sed -i 's/{{ SPT_ITEMS_HOSTNAME }}/'"$SPT_ITEMS_HOSTNAME"'/g' ./items/frontend/.env.example - mv ./items/frontend/.env.example ./items/frontend/.env - sed -i 's/{{ DEPLOY_HOSTNAME }}/'"$DEPLOY_HOSTNAME"'/g' ./.ansible-items/inventory - sed -i 's/{{ DEPLOYMENT_USER }}/'"$DEPLOYMENT_USER"'/g' ./.ansible-items/inventory ``` Executed on every push. \ The following environment variables are injected using Drone secrets: * `SPT_ITEMS_HOSTNAME` is used by the frontend to call the backend. * `DEPLOY_HOSTNAME` is used by Ansible to connect to the remote server via SSH. * `DEPLOYMENT_USER` is used by Ansible to connect to the remote server via SSH. * all environment variables at the pipeline level (see [Environment variables](#environment-variables)) Using `sed` makes temporary changes in the container/pod instead of commiting secrets in the repo in plain text. \ The changes are never pushed and are discarded when the container/pod is terminated. ### Build frontend ```yml - name: build frontend image: node:lts-alpine3.14 commands: - node -v - npm -v - yarn --version - yarn --cwd ./items/frontend install - yarn --cwd ./items/frontend build --pure-lockfile - rm -rf ./items/api/public/static/* - mv ./items/frontend/build/* ./items/api/public - rm ./items/api/public/index.html ``` Executed on every push. \ Since the PHP backend serves the ReactJS frontend, the former is built and moved in the latter. Notes: * `yarn --cwd ` executes the command in the specified file * `rm -rf ./api/public/static/*` deletes the static files to make sure no old JavaScript files remain * `rm ./api/public/index.html` ReactJS is bundled with a `index.html`. It is discarded to use [](https://dev.sp-tarkov.com/Rev/spt-items-api/raw/branch/master/resources/views/app.blade.php) instead. ### Check ansible syntax ```yml - name: check ansible syntax image: plugins/ansible:3 settings: playbook: ./.ansible-items/playbook.yml inventory: ./.ansible-items/inventory galaxy: ./.ansible-items/requirements.yml syntax_check: true ``` Executed on every push. \ Check the Ansible syntax in [playbook.yml](../.ansible/playbook.yml), [inventory](../.ansible/inventory) and [requirements.yml](../.ansible/requirements.yml). The check is executed on every push since we want to detect any error before validating the build using the promotion. ### Apply ansible playbook ```yml - name: apply ansible playbook image: plugins/ansible:3 settings: playbook: ./.ansible-items/playbook.yml inventory: ./.ansible-items/inventory galaxy: ./.ansible-items/requirements.yml private_key: from_secret: deploy_ssh_key environment: DEPLOY_HOSTNAME: from_secret: deploy_hostname SPT_ITEMS_HOSTNAME: from_secret: spt_items_hostname DEPLOYMENT_USER: from_secret: deploy_username when: branch: - master - main ``` Executed only on promotion to production. \ This step actually deploys to the server. \ This step is [idempotent](https://en.wikipedia.org/wiki/Idempotence). \ The following environment variables are injected using Drone secrets: * `SPT_ITEMS_HOSTNAME` is used by the PHP env file. * `DEPLOY_HOSTNAME` is used to connect to the remote server via SSH. * `DEPLOYMENT_USER` is used to connect to the remote server via SSH. * all environment variables at the pipeline level (see [Environment variables](#environment-variables)) #### Playbook definition ```yml hosts: host ``` Uses the host defined in [inventory](../.ansible/inventory). Remember, the step [Replace hosts and user variables](#replace-hosts-and-user-variables) already replaced the variables at this point. The playbook will be executed as `root` user using `sudo`. #### Delete old spt-items-api ```yml - name: Delete spt-items-api before adding everything again file: state: absent path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" ``` Since the copy does not override the folder, this step takes care of it. \ `SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)) #### Copy the project ```yml - name: Copy the project copy: src: ../items/api/ dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" ``` Copies the whole project (frontend and backend) from the [api](../api) folder into the server. #### Copy PHP env file ```yml - name: Copy PHP .env file template: src: ./templates/.php-env.j2 dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/.env" ``` Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP .env file](../.ansible/templates/.php_env.j2). \ `SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). \ `SPT_ITEMS_HOSTNAME` is injected in the environments properties (see [Apply ansible playbook](#apply-ansible-playbook)) #### Get JavaScript chunks name ```yml - name: Get Chunk 2 name shell: cmd: find "{{ lookup('env', 'SPT_ITEMS_PATH') }}" -type f -name "*chunk.js" -printf "%f\n" register: find_output ``` Prepare a find of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template. \ `SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). #### Get file names from find output ```yml - name: Get file names from find output set_fact: chunk_list: "{{ find_output['stdout'].split('\n') }}" ``` Splits the string containing the list of all JavaScript chunk files for the [app.blade.php.j2](../.ansible/templates/app.blade.php.j2) template. #### Copy app.blade.php file ```yml - name: Copy app.blade.php file template: src: ./templates/app.blade.php.j2 dest: "{{ lookup('env', 'SPT_ITEMS_PATH') }}/resources/views/app.blade.php" ``` Uses [Jinja2](https://jinja2docs.readthedocs.io/en/stable/) to resolve the [template for the PHP app.blade.php file](../.ansible/templates/app.blade.php.j2). \ `SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). #### Download and install composer dependencies ```yml - name: Download and installs all composer libs and dependencies community.general.composer: command: install working_dir: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" ``` #### Reset files permissions ```yml - name: Reset files permissions file: path: "{{ lookup('env', 'SPT_ITEMS_PATH') }}" owner: "{{ lookup('env', 'DEPLOYMENT_USER') }}" group: www-data mode: 0774 recurse: yes ``` Permissions 0644: * user: read/write/execute * group: read * other: read `www-data` is hardcoded here but it should be the standard user for Apache and Nginx. \ `SPT_ITEMS_PATH` is injected thanks to the pipeline level environment variables (see [Environment variables](#environment-variables)). #### Initialize database ```yml - name: Initialize database uri: url: "https://{{ lookup('env', 'SPT_ITEMS_HOSTNAME') }}/api/refresh" method: GET status_code: [200, 204] timeout: 60 ``` The call to `/api/refresh` fetches the data from AKI Server repository, `development` branch.