--- - name: Build and Upload NixOS Image hosts: localhost # Run NixOS build and image upload on the CI runner or local machine gather_facts: false # No need to gather facts for localhost vars: nixos_build_path: "{{ playbook_dir }}/../" # Path to your flake.nix result_symlink_path: "{{ nixos_build_path }}/result" # Expected symlink output from Nix dest_dir: "{{ dest_image_dir }}" # From group_vars/all.yml tasks: - name: Ensure NixOS image build script is executable ansible.builtin.file: path: "{{ playbook_dir }}/../scripts/build_nixos_image.sh" mode: "0755" - name: Build NixOS image (creates result/ symlink) ansible.builtin.command: "{{ playbook_dir }}/../scripts/build_nixos_image.sh" args: chdir: "{{ nixos_build_path }}" register: nixos_build_result changed_when: nixos_build_result.rc == 0 # Consider it changed if build succeeds - name: Get built image file (.vma.zst) from result/ ansible.builtin.find: paths: "{{ result_symlink_path }}" patterns: "*.vma.zst" file_type: file # Ensure it's a file register: built_image_files delegate_to: localhost - name: Fail if no image was built ansible.builtin.fail: msg: "No .vma.zst image file found in {{ result_symlink_path }}/" when: built_image_files.files | length == 0 delegate_to: localhost - name: Set fact for built image path and filename ansible.builtin.set_fact: local_image_path: "{{ built_image_files.files[0].path | realpath }}" image_filename: "{{ built_image_files.files[0].path | basename }}" delegate_to: localhost - name: Display paths (for debugging) ansible.builtin.debug: msg: "Local image path: {{ local_image_path }}, Filename: {{ image_filename }}" - name: Copy image to Proxmox server ansible.builtin.copy: src: "{{ local_image_path }}" dest: "{{ dest_dir }}{{ image_filename }}" mode: "0644" # Ensure correct permissions on the destination - name: Clean up local build result symlink ansible.builtin.file: path: "{{ result_symlink_path }}" state: absent delegate_to: localhost - name: Clean up local store paths (optional, for disk space on CI) ansible.builtin.command: "nix store gc" args: chdir: "{{ nixos_build_path }}" changed_when: true # Always consider it a change - name: Restore and Convert to Template on Proxmox hosts: proxmox_servers # Target the Proxmox host become: true # Need root/sudo on Proxmox host for qm commands vars: # Use variables from group_vars/all.yml and vault.yml # Access API credentials from vault for this part proxmox_api_user_id: "{{ proxmox_ci_api_user_name }}@{{ proxmox_ci_api_user_realm }}" proxmox_api_token_id: "{{ proxmox_ci_api_token_id }}" proxmox_api_token_secret: "{{ proxmox_ci_api_token_secret }}" # VM/Template specifics (can be passed via --extra-vars or from group_vars) vmid_base_template: "{{ template_vmid }}" vmname_base_template: "{{ template_vm_name }}" vmid_latest_template: "{{ latest_template_vmid }}" vmname_latest_template: "{{ latest_template_vm_name }}" # Configuration for the restored VM cpu_cores: 2 memory_mb: 2048 tasks: - name: Set full image path on Proxmox ansible.builtin.set_fact: remote_image_path: "{{ dest_image_dir }}{{ image_filename }}" delegate_to: localhost # Run this locally to set a fact accessible globally - name: Destroy existing base template VM (if it exists) ansible.builtin.shell: "qm destroy {{ vmid_base_template }} --purge || true" args: warn: no changed_when: false # Assume idempotency; if it didn't exist, no change failed_when: false # Don't fail if VM isn't found - name: Destroy existing 'latest' template VM (if it exists) ansible.builtin.shell: "qm destroy {{ vmid_latest_template }} --purge || true" args: warn: no changed_when: false failed_when: false - name: Restore VM from image to base template VMID ansible.builtin.command: > qmrestore {{ remote_image_path }} {{ vmid_base_template }} --unique true --name {{ vmname_base_template }} --storage {{ storage_name }} args: creates: "/etc/pve/qemu-server/{{ vmid_base_template }}.conf" # Idempotency check: only run if config file doesn't exist - name: Set CPU and memory for the base template VM ansible.builtin.command: > qm set {{ vmid_base_template }} --cores {{ cpu_cores }} --memory {{ memory_mb }} # This task is not fully idempotent; it will always try to set. # You'd need complex 'when' conditions to check current settings. - name: Convert base template VM to a template ansible.builtin.command: "qm template {{ vmid_base_template }}" # This command is largely idempotent for the `qm template` operation itself. - name: Clone base template to 'latest' template VMID ansible.builtin.command: > qm clone {{ vmid_base_template }} {{ vmid_latest_template }} --name {{ vmname_latest_template }} --full --storage {{ storage_name }} args: creates: "/etc/pve/qemu-server/{{ vmid_latest_template }}.conf" # Idempotency check: only run if config file doesn't exist - name: Convert 'latest' template VM to a template ansible.builtin.command: "qm template {{ vmid_latest_template }}"