Skip to content
7 changes: 7 additions & 0 deletions .github/workflows/build_and_deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ jobs:
- name: Checkout code
uses: actions/checkout@v3

# Uv is needed for the deployment lint
- name: Install uv
uses: astral-sh/setup-uv@v5

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

Expand Down Expand Up @@ -42,6 +46,9 @@ jobs:
docker run --rm intbot make ci/lint
docker run --rm intbot make ci/type-check

- name: Run deployment playbooks lint
run: make lint/deploy

services:
postgres:
image: postgres:16.4
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ CI_RUN=cd intbot && DJANGO_SETTINGS_MODULE="intbot.settings" DJANGO_ENV="ci"

# Deployment
DEPLOY_CMD=cd deploy && uvx --from "ansible-core" ansible-playbook -i hosts.yml
DEPLOY_LINT_CMD=cd deploy && uvx --from "ansible-lint" ansible-lint

# mostly useful for docker and deployment
current_git_hash=$(shell git rev-parse HEAD)
Expand Down Expand Up @@ -158,3 +159,8 @@ deploy/app:
@echo "Deploying version $(V) to a remote server"
$(DEPLOY_CMD) playbooks/03_app.yml --extra-vars "app_version=$(V)"
$(DEPLOY_CMD) playbooks/04_cron.yml

lint/deploy:
$(DEPLOY_LINT_CMD) playbooks/01_setup.yml
$(DEPLOY_LINT_CMD) playbooks/02_nginx.yml
$(DEPLOY_LINT_CMD) playbooks/03_app.yml
33 changes: 17 additions & 16 deletions deploy/playbooks/01_setup.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
- name: Deploy nginx and Let's Encrypt SSL certificate
hosts: intbot_setup
become: yes
gather_facts: yes
become: true
gather_facts: true

tasks:
- name: Install Docker dependencies
apt:
ansible.builtin.apt:
name: "{{ package }}"
state: present
update_cache: yes
update_cache: true
vars:
package:
- apt-transport-https
Expand All @@ -21,22 +21,22 @@
- name: Install Docker
block:
- name: Add Docker GPG key
apt_key:
ansible.builtin.apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present

- name: Add Docker repository
apt_repository:
ansible.builtin.apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ ansible_lsb.codename }} stable
state: present

- name: Install Docker
apt:
ansible.builtin.apt:
name: docker-ce
state: present

- name: Combine non-root users to a single list
set_fact:
ansible.builtin.set_fact:
non_root_user_names: ["{{ nginx_user }}", "{{ app_user }}"]

- name: Create non-root users
Expand All @@ -45,10 +45,10 @@
ansible.builtin.user:
name: "{{ username }}"
shell: "/bin/bash"
generate_ssh_key: yes
generate_ssh_key: true
ssh_key_type: ed25519
ssh_key_comment: "{{ username }}@{{ inventory_hostname }}"
create_home: yes
create_home: true
loop: "{{ non_root_user_names }}"
loop_control:
loop_var: username
Expand All @@ -59,36 +59,37 @@
state: directory
owner: "{{ username }}"
group: "{{ username }}"
mode: "0755"
loop: "{{ non_root_user_names }}"
loop_control:
loop_var: username

- name: Then copy the authorized_keys from root so you can ssh later to the user
copy:
ansible.builtin.copy:
src: "/root/.ssh/authorized_keys"
dest: "/home/{{ username }}/.ssh/authorized_keys"
owner: "{{ username }}"
group: "{{ username }}"
mode: "0600"
remote_src: "yes"
remote_src: true
loop: "{{ non_root_user_names }}"
loop_control:
loop_var: username

- name: Add the non root users (both nginx and app) to docker group
user:
ansible.builtin.user:
name: "{{ username }}"
groups: docker
append: yes
append: true
loop: "{{ non_root_user_names }}"
loop_control:
loop_var: username

- name: Read the deploy public key
slurp:
ansible.builtin.slurp:
src: "/home/{{ app_user }}/.ssh/id_ed25519.pub"
register: deploy_key

- name: Display the public key
debug:
ansible.builtin.debug:
msg: "For private repositories, make sure to put this key as deploy key on github: {{ deploy_key.content | b64decode }}"
16 changes: 11 additions & 5 deletions deploy/playbooks/02_nginx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,28 @@
tasks:
- name: Copy nginx configuration file
ansible.builtin.template:
src: ../templates/nginx/nginx.conf.j2
src: nginx/nginx.conf.j2
dest: ./nginx.conf
mode: "0644"

- name: Create a server Makefile (for nginx) to manage on-server tasks
ansible.builtin.template:
src: ../templates/nginx/Makefile.nginx.j2
src: nginx/Makefile.nginx.j2
dest: ./Makefile
mode: "0640"

- name: Set up docker-compose.yml on the remote server
ansible.builtin.template:
src: ../templates/nginx/docker-compose.nginx.yml.j2
src: nginx/docker-compose.nginx.yml.j2
dest: ./docker-compose.yml
mode: "0640"

- name: Make sure the directory structure for certs exist
shell: mkdir -p ./data/certbot/conf
ansible.builtin.file:
path: "/home/{{ ansible_user }}/data/certbot/conf"
state: directory
mode: "0755"

- name: Display info at the end
debug:
ansible.builtin.debug:
msg: "Go to /home/{{ ansible_user }} and run make certbot/init-staging; then make certbot/upgrade-to-prod"
46 changes: 38 additions & 8 deletions deploy/playbooks/03_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,77 @@

tasks:
- name: Clone the repository to a specific version (to a temp location)
git:
ansible.builtin.git:
repo: "{{ repository_url }}"
dest: /tmp/src
accept_hostkey: yes
accept_hostkey: true
version: "{{ app_version }}"

- name: Build with a given commit hash
# This will be stored in local registry, and available as version to docker-compose
# where we can just reference correct version
shell: "cd /tmp/src && make docker/build V={{ app_version }}"
ansible.builtin.command:
chdir: /tmp/src
cmd: "/usr/bin/make docker/build V={{ app_version }}"
register: output
changed_when: output.rc != 0
failed_when: output.rc != 0

- name: Create a server Makefile to manage app tasks
ansible.builtin.template:
src: ../templates/app/Makefile.app.j2
src: app/Makefile.app.j2
dest: ./Makefile
mode: "0640"

- name: Set up docker-compose.yml for the app
ansible.builtin.template:
src: ../templates/app/docker-compose.app.yml.j2
src: app/docker-compose.app.yml.j2
dest: ./docker-compose.yml
mode: "0640"

- name: Copy env file example
ansible.builtin.copy:
src: ../templates/app/intbot.env.example
dest: intbot.env.example
mode: "0640"

- name: Check if the env file exists
ansible.builtin.stat:
path: intbot.env
register: env_file

- name: If env file doesn't exist - copy the example
ansible.builtin.copy:
src: app/intbot.env.example
dest: intbot.env.example
mode: "0644"
when: not env_file.stat.exists

- name: If the env file doesn't exist - fail with error message
ansible.builtin.fail:
msg: "The env file doesn't exist. Please ssh, copy the example and adjust"
when: not env_file.stat.exists

- name: Start docker compose to see if everything is running
shell: "docker compose up -d"
ansible.builtin.command:
chdir: "{{ ansible_user_dir }}"
cmd: "docker compose up -d"
register: output
changed_when: output.rc != 0
failed_when: output.rc != 0

- name: Migrate on prod
shell: "make prod/migrate"
ansible.builtin.command:
chdir: "{{ ansible_user_dir }}"
cmd: "/usr/bin/make prod/migrate"
register: output
changed_when: output.rc != 0
failed_when: output.rc != 0

- name: Restart everything and finish
shell: "docker compose up -d"
ansible.builtin.command:
chdir: "{{ ansible_user_dir }}"
cmd: "docker compose up -d"
register: output
changed_when: output.rc != 0
failed_when: output.rc != 0