--- - name: "{{ app_name }} - Validate deployment requirements" block: - name: Verify Docker is available ansible.builtin.command: docker --version register: docker_check failed_when: docker_check.rc != 0 changed_when: false - name: Verify Docker Compose is available ansible.builtin.command: docker compose version register: compose_check failed_when: compose_check.rc != 0 changed_when: false - name: Validate required variables ansible.builtin.assert: that: - app_name is defined - app_name | length > 0 - app_name is match('^[a-zA-Z0-9_.-]+$') - host_root_path is defined - host_root_path | length > 0 - host_root_path is match('^/[a-zA-Z0-9./_-]*$') - not (host_root_path is match('.*\.\..*')) fail_msg: "Validation failed for {{ app_name | default('undefined') }}: Required variables missing or contain invalid characters. app_name and host_root_path must be defined, contain only alphanumeric, dot, underscore, hyphen, and slash characters, and must not contain path traversal sequences." - name: Set compose source type ansible.builtin.set_fact: _compose_type: >- {{ 'template' if app_compose_template is not none else 'file' if app_compose_file is not none else 'content' if app_compose_content is not none else 'none' }} - name: Validate compose source ansible.builtin.assert: that: - _compose_type != 'none' fail_msg: "No Docker Compose source specified for {{ app_name }}: Must define one of app_compose_template, app_compose_file, or app_compose_content" - name: Validate directory paths ansible.builtin.assert: that: - app_dir is match('^/[a-zA-Z0-9./_-]*$') - not (app_dir is match('.*\.\..*')) fail_msg: "Security validation failed for {{ app_name }}: app_dir '{{ app_dir }}' contains invalid characters or path traversal sequences" - name: Check if compose file exists ansible.builtin.stat: path: "{{ app_compose_file }}" register: _compose_file_stat delegate_to: localhost become: false when: app_compose_file is defined and app_compose_file - name: Assert compose file exists ansible.builtin.assert: that: - _compose_file_stat.stat.exists fail_msg: "Docker Compose file not found for {{ app_name }}: '{{ app_compose_file }}' does not exist" when: app_compose_file is defined and app_compose_file tags: [setup, deploy] - name: "{{ app_name }} - Create application directories" block: - name: Create application directory ansible.builtin.file: path: "{{ app_dir }}" state: directory owner: "{{ app_uid }}" group: "{{ app_gid }}" mode: "{{ app_dir_mode }}" # Items may be plain path strings, or dicts with per-item overrides: # - /some/path # uses app_uid/app_gid/app_dir_mode # - path: /some/path # same # owner: 999 # per-item overrides for containers # group: 999 # that expect a specific uid/gid # mode: "0700" - name: Create application subdirectories ansible.builtin.file: path: "{{ item.path | default(item) }}" state: directory owner: "{{ item.owner | default(app_uid) }}" group: "{{ item.group | default(app_gid) }}" mode: "{{ item.mode | default(app_dir_mode) }}" loop: "{{ app_subdirectories }}" tags: [setup, deploy] - name: "{{ app_name }} - Deploy additional templates" block: - name: Copy over additional templates ansible.builtin.template: src: "{{ item.src }}" dest: "{{ item.dest }}" owner: "{{ app_uid }}" group: "{{ app_gid }}" mode: "{{ app_permission_mode }}" loop: "{{ app_extra_templates }}" tags: [setup, deploy]