---
- name: Deploy impress from the current local version
  hosts: webserver
  become: yes
  become_user: impress
  vars:
    local_app_root: "{{ playbook_dir  }}/.."
    remote_project_root: "/srv/impress"
    ruby_version: "3.3.5"

    # deploy:setup should have added us to the "impress-deployers" group, so we
    # should be able to become the "impress" user without a password.
    ansible_become_password: ""

    # Set this to `yes` to skip setting this version as `current`. (We also
    # skip our usual cleanup, to avoid disrupting the `current` version!)
    #
    # This can be useful for upgrading our Ruby version without downtime:
    # 1. Install the new Ruby version, but don't uninstall the old one.
    # 2. Update the app to reference the new Ruby version in the `Gemfile`.
    # 3. Deploy the app with `skip_set_as_current`.
    # 4. Update the service file manually to use the new Ruby to run the new
    #    version of the app, referenced directly by path.
    # 5. Link the new version as `current` manually.
    # 6. Reset the service file to use the new Ruby to run `current`.
    skip_set_as_current: no
  tasks:
    - 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_project_root }}/versions/{{ new_app_version.stdout }}"

    - name: Create new remote folder for the new version
      file:
        path: "{{ remote_app_root }}"
        state: directory

    # NOTE: We skip most gitignored files, except for public/assets/*, which
    # contains the assets we precompiled for production.
    - name: Copy local app's source files to new remote folder
      ansible.posix.synchronize:
        src: "{{ local_app_root }}/"
        dest: "{{ remote_app_root }}"
        rsync_opts:
          - "--include=/public/assets/*"
          - "--exclude=.git"
          - "--filter=':- .gitignore'"

    - name: Link the public-data folder to the shared public-data folder
      file:
        src: "{{ remote_project_root }}/shared/public-data"
        dest: "{{ remote_app_root }}/public/public-data"
        state: link

    - name: Configure Bundler to run in deployment mode
      command:
        chdir: "{{ remote_app_root }}"
        cmd: /opt/ruby-{{ ruby_version }}/bin/bundle config set --local deployment true

    # This ensures that, while attempting our current deploy, we don't
    # accidentally delete gems out from under the currently-running version.
    # NOTE: From reading the docs, I thiink this is the default behavior, but
    # I can't be sure? Rather than deep-dive to find out, I'd rather just set
    # it, to be clear about the default(?) behavior we're depending on.
    - name: Configure Bundler to *not* clean up old gems when installing
      command:
        chdir: "{{ remote_app_root }}"
        cmd: /opt/ruby-{{ ruby_version }}/bin/bundle config set --local clean false

    # NOTE: Bundler recommends this, and they're pretty smart about it: if the
    # Gemfile changes, this shouldn't disrupt the currently-running version,
    # because we won't clean up its now-unused gems yet, and if we upgrade a
    # gem it'll install *both* versions of the gem until we clean up.
    - name: Configure Bundler to use the bundle folder shared by all app versions
      command:
        chdir: "{{ remote_app_root }}"
        cmd: "/opt/ruby-{{ ruby_version }}/bin/bundle config set --local path {{ remote_project_root}}/shared/bundle"

    - name: Run `bundle install` to install dependencies in remote folder
      command:
        chdir: "{{ remote_app_root }}"
        # The `--local` flag instructs Bundler to use the cached dependencies
        # in `vendor/cache`, instead of reading from the web, which is much
        # faster and more reliable!
        cmd: /opt/ruby-{{ ruby_version }}/bin/bundle install --local

    - name: Update the `current` folder to point to the new version
      file:
        src: "{{ remote_app_root }}"
        dest: /srv/impress/current
        state: link
      when: not skip_set_as_current

    # NOTE: This uses the passwordless sudo rule we set up in deploy:setup.
    # We write it as a command rather than using the built-in `systemd` Ansible
    # module, to make sure we're invoking it exactly as we wrote in that rule.
    #
    # NOTE: We use `sudo` instead of `become_user: root`, because we don't have
    # permission to *become* the root user; we only have permission to run this
    # one command as them.
    - name: Restart the app
      become: no
      command: sudo systemctl restart impress
      when: not skip_set_as_current

    - name: Clean up gems no longer used in the current app version
      command:
        chdir: "{{ remote_app_root }}"
        cmd: /opt/ruby-{{ ruby_version }}/bin/bundle clean
      when: not skip_set_as_current

    - name: Find older app versions to clean up
      # Print out all but the 5 last-recently-updated versions.
      command:
        chdir: "{{ remote_project_root }}/versions"
        cmd: bash -c 'ls -t | tail -n +6'
      register: versions_to_clean_up
      when: not skip_set_as_current

    - name: Clean up older versions
      file:
        path: "{{ remote_project_root }}/versions/{{ item }}"
        state: absent
      with_items: "{{ versions_to_clean_up.stdout_lines }}"
      when: not skip_set_as_current