Matchu
93abd1fa11
Ok I think this oughta do it! A bug we used to have was that, if you deployed 5 times in a row without changes to node_modules, then all 5 versions would reference `node_modules` from 6 deploys ago, which would then get "cleaned up". And so then the app would have all its deps disappear! There's still a bug here, noted in the comments. That one sounds harder to solve, and suggests a refactor for how to do build caching more robustly, and… idk I think this cheat might just not be worth it. Idk! But I'm keeping it for the moment for convenience.
113 lines
4.8 KiB
YAML
113 lines
4.8 KiB
YAML
---
|
|
- name: Deploy impress-2020 from the current local version
|
|
hosts: webserver
|
|
vars:
|
|
skip_build: no # Can override this by running `yarn deploy-skip-build`
|
|
local_app_root: "{{ playbook_dir }}/../.."
|
|
remote_versions_root: "/srv/impress-2020/versions"
|
|
tasks:
|
|
# For our deployment, our server resources are more constrained than our
|
|
# dev machine, and builds are an expensive uncommon RAM & CPU spike. Let's
|
|
# use our local machine for it! (I found that, running remotely, 2GB RAM
|
|
# was not enough to build impress-2020 😅)
|
|
- name: Run `yarn build` locally to build the current version
|
|
delegate_to: localhost
|
|
command:
|
|
chdir: "{{ local_app_root }}"
|
|
cmd: yarn build
|
|
when: not skip_build
|
|
|
|
- name: Generate a version name from the current timestamp
|
|
command: date '+%Y-%m-%d-%s'
|
|
register: new_app_version
|
|
|
|
- name: Print out the new version name
|
|
debug:
|
|
msg: "Deploying new version: {{ new_app_version.stdout }}"
|
|
|
|
- name: Save new remote folder path to a variable
|
|
set_fact:
|
|
remote_app_root: "{{ remote_versions_root }}/{{ new_app_version.stdout }}"
|
|
|
|
- name: Create new remote folder for the new version
|
|
file:
|
|
path: "{{ remote_app_root }}"
|
|
state: directory
|
|
|
|
- name: Copy local app's source files to new remote folder
|
|
ansible.posix.synchronize:
|
|
src: "{{ local_app_root }}/"
|
|
dest: "{{ remote_app_root }}"
|
|
rsync_opts:
|
|
- "--exclude=.git"
|
|
- "--filter=':- .gitignore'"
|
|
|
|
- name: Copy local app's .next build files to new remote folder
|
|
ansible.posix.synchronize:
|
|
src: "{{ local_app_root }}/.next/"
|
|
dest: "{{ remote_app_root }}/.next"
|
|
|
|
- name: Copy local app's .env secrets file to new remote folder
|
|
ansible.posix.synchronize:
|
|
src: "{{ local_app_root }}/.env"
|
|
dest: "{{ remote_app_root }}/.env"
|
|
|
|
- name: Check whether we can reuse the currently deployed node_modules.
|
|
# First, compare the files. Then, ensure that node_modules is in a good
|
|
# state and will be linkable. Finally, ensure that it's one of the 5 most
|
|
# recent versions, so it won't be cleaned up. If this succeeds, we can do
|
|
# the link cheat! If this fails, proceed and run `yarn install` instead.
|
|
command: |
|
|
bash -c "
|
|
cmp '{{ remote_app_root }}/yarn.lock' '/srv/impress-2020/current/yarn.lock' &&
|
|
test -e /srv/impress-2020/current/node_modules &&
|
|
(ls -dt /srv/impress-2020/versions/* | head -n 5 | grep $(realpath /srv/impress-2020/current/node_modules/..))"
|
|
failed_when: no
|
|
register: can_reuse_node_modules_when_successful
|
|
|
|
- name: Link node_modules to current deployed version, if they match
|
|
# Linking is a pretty wild cheat to make this much faster than copying!
|
|
# We're counting on the assumptions that 1) versions' dependencies don't
|
|
# change after the fact, and 2) the app doesn't mutate node_modules while
|
|
# running. So, these two copies of node_modules *should* just stay
|
|
# permanently the way they are forever, so linking shoouulld be safe 🤞
|
|
command: |
|
|
bash -c "ln -s $(realpath /srv/impress-2020/current/node_modules) {{ remote_app_root }}/node_modules"
|
|
when: "can_reuse_node_modules_when_successful.rc == 0"
|
|
|
|
- name: Run `yarn install` to install dependencies in new remote folder
|
|
command:
|
|
chdir: "{{ remote_app_root }}"
|
|
cmd: yarn install
|
|
when: "can_reuse_node_modules_when_successful.rc > 0"
|
|
|
|
- name: Update the `current` folder to point to the new version
|
|
file:
|
|
src: "{{ remote_app_root }}"
|
|
dest: /srv/impress-2020/current
|
|
state: link
|
|
|
|
- name: Reload the app in pm2
|
|
command: pm2 reload impress-2020
|
|
|
|
- name: Find older versions to clean up
|
|
# Print out all but the 5 last-recently-updated versions.
|
|
# NOTE: If we change this count, change the count we use when checking
|
|
# whether to reuse node_modules too!
|
|
# TODO: This *will* still break *older* versions that pointed to an old
|
|
# `node_modules` that got cleaned up, which could make rollback a
|
|
# pain. We don't roll back often for DTI so that's probably fine,
|
|
# but it'd be better to do something a bit more robust - or just
|
|
# drop it and accept the install perf hit every time. (It's only
|
|
# like 40 seconds on this last test, which isn't as bad as I
|
|
# remember, especially when deploys aren't super common.)
|
|
command:
|
|
chdir: "{{ remote_versions_root }}"
|
|
cmd: bash -c 'ls -t | tail -n +6'
|
|
register: versions_to_clean_up
|
|
|
|
- name: Clean up older versions
|
|
file:
|
|
path: "{{ remote_versions_root }}/{{ item }}"
|
|
state: absent
|
|
with_items: "{{ versions_to_clean_up.stdout_lines }}"
|