--- - name: Resolve runner profile ansible.builtin.set_fact: __windows_gitea_runner_profile: >- {{ windows_gitea_runner_runner.profile | default(windows_gitea_runner_runner.name | default('runner')) }} - name: Resolve runner-specific settings ansible.builtin.set_fact: __windows_gitea_runner_name: >- {{ windows_gitea_runner_runner.name | default( 'gitea-runner-' ~ __windows_gitea_runner_profile ) }} __windows_gitea_runner_container_name: >- {{ windows_gitea_runner_runner.container_name | default( 'gitea-runner-' ~ __windows_gitea_runner_profile ) }} __windows_gitea_runner_image: >- {{ windows_gitea_runner_runner.image | default( windows_gitea_runner_image_prefix ~ ':' ~ __windows_gitea_runner_profile ) }} __windows_gitea_runner_labels: >- {{ windows_gitea_runner_runner.labels | default( __windows_gitea_runner_profile ~ ':host' ) }} __windows_gitea_runner_dockerfile_src: >- {{ windows_gitea_runner_runner.dockerfile_src | default( 'Dockerfile.' ~ __windows_gitea_runner_profile ) }} __windows_gitea_runner_config_file: >- {{ windows_gitea_runner_runner.config_file | default( windows_gitea_runner_config_root ~ '\\' ~ __windows_gitea_runner_profile ~ '\\config.yaml' ) }} __windows_gitea_runner_config_dir: >- {{ ( windows_gitea_runner_runner.config_file | default( windows_gitea_runner_config_root ~ '\\' ~ __windows_gitea_runner_profile ~ '\\config.yaml' ) ) | regex_replace('\\\\[^\\\\]+$', '') }} __windows_gitea_runner_config_name: >- {{ ( windows_gitea_runner_runner.config_file | default( windows_gitea_runner_config_root ~ '\\' ~ __windows_gitea_runner_profile ~ '\\config.yaml' ) ) | regex_search('[^\\\\]+$') }} __windows_gitea_runner_data_dir: >- {{ windows_gitea_runner_runner.data_dir | default( windows_gitea_runner_data_root ~ '\\' ~ __windows_gitea_runner_profile ~ '\\data' ) }} __windows_gitea_runner_log_dir: >- {{ windows_gitea_runner_runner.log_dir | default( windows_gitea_runner_log_root ~ '\\' ~ __windows_gitea_runner_profile ) }} __windows_gitea_runner_build_dir: >- {{ windows_gitea_runner_build_root ~ '\\' ~ __windows_gitea_runner_profile }} __windows_gitea_runner_build_dockerfile_dest: >- {{ windows_gitea_runner_build_root ~ '\\' ~ __windows_gitea_runner_profile ~ '\\Dockerfile' }} __windows_gitea_runner_dockerfile_source_path: >- {{ windows_gitea_runner_dockerfile_source_root ~ '/' ~ ( windows_gitea_runner_runner.dockerfile_src | default( 'Dockerfile.' ~ __windows_gitea_runner_profile ) ) }} __windows_gitea_runner_enable_nested_build: >- {{ windows_gitea_runner_runner.enable_nested_build | default(windows_gitea_runner_container_enable_nested_build) }} __windows_gitea_runner_storage_dir: >- {{ ( windows_gitea_runner_runner.storage_dir | default( windows_gitea_runner_container_storage_root ~ '\\' ~ __windows_gitea_runner_profile ) ) if ( windows_gitea_runner_runner.enable_nested_build | default(windows_gitea_runner_container_enable_nested_build) ) else '' }} __windows_gitea_runner_run_args: >- {{ windows_gitea_runner_runner.run_args | default(windows_gitea_runner_container_run_args) }} - name: Resolve runner runtime config path ansible.builtin.set_fact: __windows_gitea_runner_runtime_config_file: "{{ __windows_gitea_runner_data_dir }}\\config.yaml" - name: Read runner Dockerfile checksum ansible.builtin.set_fact: __windows_gitea_runner_base_dockerfile_checksum: >- {{ lookup( 'file', windows_gitea_runner_dockerfile_source_root ~ '/' ~ windows_gitea_runner_base_dockerfile_src ) | hash('sha1') }} __windows_gitea_runner_profile_dockerfile_checksum: >- {{ lookup('file', __windows_gitea_runner_dockerfile_source_path) | hash('sha1') }} - name: Compute desired runner image hash ansible.builtin.set_fact: __windows_gitea_runner_desired_image_hash: >- {{ { 'base': __windows_gitea_runner_base_dockerfile_checksum, 'profile': __windows_gitea_runner_profile_dockerfile_checksum } | to_json | hash('sha1') }} - name: Compute desired runner container hash ansible.builtin.set_fact: __windows_gitea_runner_desired_container_hash: >- {{ { 'image': __windows_gitea_runner_image, 'name': __windows_gitea_runner_name, 'labels': __windows_gitea_runner_labels, 'instance': windows_gitea_runner_instance_url, 'config': __windows_gitea_runner_runtime_config_file, 'config_dir': __windows_gitea_runner_data_dir, 'config_name': ( (__windows_gitea_runner_runtime_config_file | regex_search('[^\\\\]+$')) | default('config.yaml', true) ), 'config_mount_mode': 'data', 'data': __windows_gitea_runner_data_dir, 'log': __windows_gitea_runner_log_dir, 'nested_build': __windows_gitea_runner_enable_nested_build, 'storage': __windows_gitea_runner_storage_dir, 'run_args': __windows_gitea_runner_run_args, 'restart': windows_gitea_runner_restart_policy, 'image_hash': __windows_gitea_runner_desired_image_hash } | to_json | hash('sha1') }} - name: Ensure runner directories exist ansible.windows.win_file: path: "{{ item }}" state: directory loop: - "{{ windows_gitea_runner_build_root }}" - "{{ __windows_gitea_runner_build_dir }}" - "{{ __windows_gitea_runner_data_dir }}" - "{{ __windows_gitea_runner_log_dir }}" - "{{ __windows_gitea_runner_config_dir }}" - name: Ensure nested build storage directory exists ansible.windows.win_file: path: "{{ __windows_gitea_runner_storage_dir }}" state: directory when: __windows_gitea_runner_enable_nested_build - name: Grant podman service user access to runner directories ansible.windows.win_acl: path: "{{ item }}" user: ".\\{{ windows_gitea_runner_podman_service_user }}" rights: FullControl type: allow state: present inherit: ContainerInherit, ObjectInherit propagation: None loop: - "{{ windows_gitea_runner_build_root }}" - "{{ __windows_gitea_runner_data_dir }}" - "{{ __windows_gitea_runner_log_dir }}" - "{{ __windows_gitea_runner_config_dir }}" become: true become_method: ansible.builtin.runas become_user: SYSTEM - name: Grant podman service user access to nested build storage directory ansible.windows.win_acl: path: "{{ __windows_gitea_runner_storage_dir }}" user: ".\\{{ windows_gitea_runner_podman_service_user }}" rights: FullControl type: allow state: present inherit: ContainerInherit, ObjectInherit propagation: None become: true become_method: ansible.builtin.runas become_user: SYSTEM when: __windows_gitea_runner_enable_nested_build - name: Render runner config ansible.windows.win_template: src: config.yaml.j2 dest: "{{ __windows_gitea_runner_runtime_config_file }}" - name: Copy runner Dockerfile to build context ansible.windows.win_copy: src: "{{ __windows_gitea_runner_dockerfile_source_path }}" dest: "{{ __windows_gitea_runner_build_dockerfile_dest }}" - name: Read current runner image hash label ansible.windows.win_shell: | $ErrorActionPreference = "Stop" $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" $image = "{{ __windows_gitea_runner_image }}" $null = & $podman image exists $image 2>$null if ($LASTEXITCODE -ne 0) { Write-Output "__MISSING__" exit 0 } $raw = & $podman image inspect $image --format json 2>$null if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($raw)) { Write-Output "__MISSING__" exit 0 } $obj = $raw | ConvertFrom-Json if ($obj -is [System.Array]) { $obj = $obj[0] } $labels = $obj.Config.Labels if ($null -eq $labels) { Write-Output "__UNLABELED__" exit 0 } $key = "{{ windows_gitea_runner_image_hash_label }}" $current = $labels.$key if ([string]::IsNullOrWhiteSpace($current)) { Write-Output "__UNLABELED__" exit 0 } Write-Output $current.Trim() become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_current_image_hash changed_when: false - name: Determine if runner image needs rebuild ansible.builtin.set_fact: __windows_gitea_runner_needs_image_rebuild: >- {{ (__windows_gitea_runner_current_image_hash.stdout | trim) != (__windows_gitea_runner_desired_image_hash | trim) }} - name: Rebuild runner image when Dockerfile changed ansible.windows.win_shell: | $ErrorActionPreference = "Stop" $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" & $podman build ` --label "{{ windows_gitea_runner_image_hash_label }}={{ __windows_gitea_runner_desired_image_hash }}" ` --tag "{{ __windows_gitea_runner_image }}" ` --file "{{ __windows_gitea_runner_build_dockerfile_dest }}" ` "{{ __windows_gitea_runner_build_dir }}" if ($LASTEXITCODE -ne 0) { exit 1 } Write-Output "__BUILT__" become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_build changed_when: "'__BUILT__' in __windows_gitea_runner_build.stdout" when: __windows_gitea_runner_needs_image_rebuild - name: Read current runner container hash label ansible.windows.win_shell: | $ErrorActionPreference = "Stop" $name = "{{ __windows_gitea_runner_container_name }}" $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" $null = & $podman container exists $name 2>$null if ($LASTEXITCODE -ne 0) { Write-Output "__MISSING__" exit 0 } $raw = & $podman inspect $name --format json 2>$null if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($raw)) { Write-Output "__MISSING__" exit 0 } $obj = $raw | ConvertFrom-Json if ($obj -is [System.Array]) { $obj = $obj[0] } $labels = $obj.Config.Labels if ($null -eq $labels) { Write-Output "__UNLABELED__" exit 0 } $key = "{{ windows_gitea_runner_container_hash_label }}" $current = $labels.$key if ([string]::IsNullOrWhiteSpace($current)) { Write-Output "__UNLABELED__" exit 0 } Write-Output $current.Trim() become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_current_hash changed_when: false - name: Determine if runner container needs recreation ansible.builtin.set_fact: __windows_gitea_runner_needs_recreate: >- {{ (__windows_gitea_runner_current_hash.stdout | trim) != (__windows_gitea_runner_desired_container_hash | trim) }} - name: Remove stale runner container before recreate ansible.windows.win_shell: | $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" & $podman rm -f "{{ __windows_gitea_runner_container_name }}" 2>$null become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" when: __windows_gitea_runner_needs_recreate changed_when: true failed_when: false - name: Ensure runner container is running ansible.windows.win_shell: | $ErrorActionPreference = "Stop" $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" $name = "{{ __windows_gitea_runner_container_name }}" $exists = & $podman container exists $name if ($LASTEXITCODE -eq 0) { & $podman start $name 2>$null | Out-Null Write-Output "__STARTED_EXISTING__" exit 0 } & $podman run -d --name $name --restart "{{ windows_gitea_runner_restart_policy }}" ` @({{ __windows_gitea_runner_run_args | map('to_json') | join(', ') }}) ` --label "{{ windows_gitea_runner_container_hash_label }}={{ __windows_gitea_runner_desired_container_hash }}" ` -v "{{ __windows_gitea_runner_data_dir }}:/data" ` -e "CONFIG_FILE=/data/config.yaml" ` -e "GITEA_INSTANCE_URL={{ windows_gitea_runner_instance_url }}" ` -e "GITEA_RUNNER_REGISTRATION_TOKEN={{ windows_gitea_runner_registration_token }}" ` -e "GITEA_RUNNER_NAME={{ __windows_gitea_runner_name }}" ` -e "GITEA_RUNNER_LABELS={{ __windows_gitea_runner_labels }}" ` {% if __windows_gitea_runner_enable_nested_build %} -v "{{ __windows_gitea_runner_storage_dir }}:/var/lib/containers" ` -e "BUILDAH_ISOLATION=chroot" ` -e "STORAGE_DRIVER=vfs" ` {% endif %} "{{ __windows_gitea_runner_image }}" | Out-Null if ($LASTEXITCODE -ne 0) { exit 1 } Write-Output "__CREATED__" become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_container changed_when: "'__CREATED__' in __windows_gitea_runner_container.stdout" no_log: true - name: Verify runner container state ansible.windows.win_shell: | $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" & $podman inspect "{{ __windows_gitea_runner_container_name }}" --format "{{ '{{' }}.State.Status{{ '}}' }}" become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_status changed_when: false failed_when: (__windows_gitea_runner_status.stdout | trim) != 'running' - name: Show tail of runner logs ansible.windows.win_shell: | $podman = "{{ windows_gitea_runner_podman_install_dir }}\podman.exe" & $podman logs --tail {{ windows_gitea_runner_container_log_tail }} "{{ __windows_gitea_runner_container_name }}" become: true become_method: ansible.builtin.runas become_user: "{{ windows_gitea_runner_podman_service_user }}" register: __windows_gitea_runner_logs changed_when: false - name: Print runner logs ansible.builtin.debug: msg: "{{ __windows_gitea_runner_logs.stdout | default('') }}" - name: Build normalized runner log text ansible.builtin.set_fact: __windows_gitea_runner_logs_all: >- {{ [ __windows_gitea_runner_logs.stdout | default(''), __windows_gitea_runner_logs.stderr | default('') ] | join('\n') | lower }} - name: Assert runner did not fail registration ansible.builtin.assert: that: - "'invalid token' not in __windows_gitea_runner_logs_all" - "'unauthorized' not in __windows_gitea_runner_logs_all" - "'open config file' not in __windows_gitea_runner_logs_all" - "'is a directory' not in __windows_gitea_runner_logs_all" fail_msg: >- Gitea runner logs indicate a registration/startup failure. Check token, instance URL, and config mount path.