--- - name: Prepare the isolated rootless Atlas Gitea target tags: [atlas, gitea] when: atlas_manage_gitea | bool block: - name: Require the existing Atlas application-data dataset ansible.builtin.assert: that: - atlas_manage_storage | bool - atlas_gitea_dataset == atlas_zfs_pool ~ '/services/data/gitea' - atlas_gitea_mountpoint == atlas_app_data_mountpoint ~ '/gitea' - atlas_gitea_username == atlas_admin_username - atlas_gitea_group == atlas_admin_group - atlas_gitea_uid | int == atlas_admin_uid | int - atlas_gitea_gid | int == atlas_admin_gid | int - atlas_gitea_container_uid | int == 1000 - atlas_gitea_container_gid | int == 1000 - atlas_gitea_staging_bind_address == '127.0.0.1' - not (atlas_gitea_production_enabled | bool) or atlas_manage_firewall | bool - not (atlas_gitea_production_enabled | bool) or atlas_gitea_bind_address == ansible_host fail_msg: >- Rootless Gitea requires Atlas storage, the admin user manager, the dedicated dataset, and loopback-only staging ports. - name: Inspect the final-restore marker before production activation ansible.builtin.stat: path: "{{ atlas_gitea_mountpoint }}/.final-sha256" register: atlas_gitea_final_marker when: atlas_gitea_production_enabled | bool - name: Refuse production activation without the final consistent restore ansible.builtin.assert: that: - atlas_gitea_final_marker.stat.isreg | default(false) fail_msg: Restore the final stopped-source Gitea export before enabling production. when: atlas_gitea_production_enabled | bool - name: Verify the production Gitea dataset belongs to admin ansible.builtin.stat: path: "{{ atlas_gitea_mountpoint }}" register: atlas_gitea_dataset_owner when: atlas_gitea_production_enabled | bool - name: Refuse to overlap the legacy host-account service ansible.builtin.assert: that: - atlas_gitea_dataset_owner.stat.uid | int == atlas_admin_uid | int - atlas_gitea_dataset_owner.stat.gid | int == atlas_admin_gid | int fail_msg: >- Run the explicit Gitea owner migration before enabling the admin Quadlet; never chown an active legacy service in a normal run. when: atlas_gitea_production_enabled | bool - name: Remove the retired account's parent-dataset traverse ACL ansible.posix.acl: path: "{{ item }}" etype: user entity: "{{ atlas_gitea_legacy_username }}" state: absent loop: - "{{ atlas_services_mountpoint }}" - "{{ atlas_app_data_mountpoint }}" when: atlas_gitea_production_enabled | bool - name: Enable POSIX ACLs only on the service-namespace parents community.general.zfs: name: "{{ item }}" state: present extra_zfs_properties: acltype: posix loop: - "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_services }}" - "{{ atlas_zfs_pool }}/{{ atlas_zfs_dataset_app_data }}" - name: Create the dedicated Gitea ZFS dataset community.general.zfs: name: "{{ atlas_gitea_dataset }}" state: present extra_zfs_properties: compression: zstd mountpoint: "{{ atlas_gitea_mountpoint }}" - name: Restrict the Gitea dataset and create rootless volume paths ansible.builtin.file: path: "{{ item }}" state: directory owner: "{{ atlas_gitea_username }}" group: "{{ atlas_gitea_group }}" mode: "0700" loop: - "{{ atlas_gitea_mountpoint }}" - "{{ atlas_gitea_mountpoint }}/data" - "{{ atlas_gitea_mountpoint }}/config" - "{{ atlas_gitea_home }}/.config" - "{{ atlas_gitea_home }}/.config/containers" - "{{ atlas_gitea_quadlet_dir }}" - name: Ensure lingering for the admin rootless account ansible.builtin.command: argv: - loginctl - enable-linger - "{{ atlas_gitea_username }}" creates: "/var/lib/systemd/linger/{{ atlas_gitea_username }}" - name: Start the admin rootless user manager ansible.builtin.systemd: name: "user@{{ atlas_gitea_uid }}.service" state: started when: not ansible_check_mode - name: Prepare the admin-owned Gitea image ansible.builtin.import_tasks: gitea_image.yml - name: Render the rootless Gitea Quadlet ansible.builtin.template: src: atlas-gitea.container.j2 dest: "{{ atlas_gitea_quadlet_dir }}/atlas-gitea.container" owner: "{{ atlas_gitea_username }}" group: "{{ atlas_gitea_group }}" mode: "0644" - name: Permit only Aegis to reach production Gitea HTTP and SSH ansible.posix.firewalld: rich_rule: >- rule family="ipv4" source address="{{ atlas_aegis_ip }}" port port="{{ item }}" protocol="tcp" accept zone: "{{ atlas_firewalld_zone }}" state: "{{ 'enabled' if atlas_gitea_production_enabled | bool else 'disabled' }}" permanent: true immediate: true loop: - "{{ atlas_gitea_http_port }}" - "{{ atlas_gitea_ssh_port }}" when: atlas_manage_firewall | bool - name: Reload the rootless Gitea user manager without starting Gitea become_user: "{{ atlas_gitea_username }}" ansible.builtin.systemd: scope: user daemon_reload: true environment: XDG_RUNTIME_DIR: "/run/user/{{ atlas_gitea_uid }}" DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ atlas_gitea_uid }}/bus" when: not ansible_check_mode - name: Start and enable the rootless Gitea user Quadlet after final restore become_user: "{{ atlas_gitea_username }}" ansible.builtin.systemd: name: atlas-gitea.service scope: user state: started enabled: true environment: XDG_RUNTIME_DIR: "/run/user/{{ atlas_gitea_uid }}" DBUS_SESSION_BUS_ADDRESS: "unix:path=/run/user/{{ atlas_gitea_uid }}/bus" when: - atlas_gitea_production_enabled | bool - not ansible_check_mode