thingies
This commit is contained in:
parent
08281ce349
commit
c6e1a01167
5 changed files with 435 additions and 0 deletions
|
|
@ -25,6 +25,7 @@
|
||||||
name:
|
name:
|
||||||
- ca-certificates
|
- ca-certificates
|
||||||
- curl
|
- curl
|
||||||
|
- gnupg
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
- name: Create directory for Docker GPG key
|
- name: Create directory for Docker GPG key
|
||||||
|
|
|
||||||
|
|
@ -26,3 +26,8 @@ bitcoin_rpc_password: "CHANGE_ME_TO_SECURE_PASSWORD"
|
||||||
# Mempool MariaDB credentials
|
# Mempool MariaDB credentials
|
||||||
# Used by: services/mempool/deploy_mempool_playbook.yml
|
# Used by: services/mempool/deploy_mempool_playbook.yml
|
||||||
mariadb_mempool_password: "CHANGE_ME_TO_SECURE_PASSWORD"
|
mariadb_mempool_password: "CHANGE_ME_TO_SECURE_PASSWORD"
|
||||||
|
|
||||||
|
# Forgejo Runner registration token
|
||||||
|
# Used by: services/forgejo-runner/deploy_forgejo_runner_playbook.yml
|
||||||
|
# See: services/forgejo-runner/SETUP.md for how to obtain this token
|
||||||
|
forgejo_runner_registration_token: "YOUR_RUNNER_TOKEN_HERE"
|
||||||
|
|
|
||||||
28
ansible/services/forgejo-runner/SETUP.md
Normal file
28
ansible/services/forgejo-runner/SETUP.md
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Forgejo Runner Setup
|
||||||
|
|
||||||
|
## Obtaining the Registration Token
|
||||||
|
|
||||||
|
1. Log in to the Forgejo instance at `https://forgejo.contrapeso.xyz`
|
||||||
|
2. Go to **Site Administration** > **Actions** > **Runners**
|
||||||
|
3. Click **Create new runner**
|
||||||
|
4. Copy the registration token
|
||||||
|
|
||||||
|
## Configuring the Token
|
||||||
|
|
||||||
|
Paste the token into `ansible/infra_secrets.yml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
forgejo_runner_registration_token: "YOUR_TOKEN_HERE"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Playbook
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ansible-playbook ansible/services/forgejo-runner/deploy_forgejo_runner_playbook.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verifying
|
||||||
|
|
||||||
|
1. On the VM: `systemctl status forgejo-runner` should show active
|
||||||
|
2. In Forgejo: **Site Administration** > **Actions** > **Runners** should show the runner as online
|
||||||
|
3. In Uptime Kuma: the `forgejo-runner-healthcheck` push monitor should be receiving pings
|
||||||
|
|
@ -0,0 +1,392 @@
|
||||||
|
- name: Install Forgejo Runner on Debian 13
|
||||||
|
hosts: forgejo_runner_local
|
||||||
|
become: yes
|
||||||
|
vars_files:
|
||||||
|
- ../../infra_vars.yml
|
||||||
|
- ../../services_config.yml
|
||||||
|
- ../../infra_secrets.yml
|
||||||
|
- ./forgejo_runner_vars.yml
|
||||||
|
vars:
|
||||||
|
uptime_kuma_api_url: "https://{{ subdomains.uptime_kuma }}.{{ root_domain }}"
|
||||||
|
ntfy_topic: "{{ service_settings.ntfy.topic }}"
|
||||||
|
healthcheck_interval_seconds: 60
|
||||||
|
healthcheck_timeout_seconds: 90
|
||||||
|
healthcheck_retries: 1
|
||||||
|
healthcheck_script_dir: /opt/forgejo-runner-healthcheck
|
||||||
|
healthcheck_script_path: "{{ healthcheck_script_dir }}/forgejo_runner_healthcheck.sh"
|
||||||
|
healthcheck_log_file: "{{ healthcheck_script_dir }}/forgejo_runner_healthcheck.log"
|
||||||
|
healthcheck_service_name: forgejo-runner-healthcheck
|
||||||
|
|
||||||
|
tasks:
|
||||||
|
# ── 1. Assert Docker is available ──────────────────────────────────
|
||||||
|
- name: Check if Docker is installed
|
||||||
|
command: docker --version
|
||||||
|
register: docker_check
|
||||||
|
changed_when: false
|
||||||
|
failed_when: docker_check.rc != 0
|
||||||
|
|
||||||
|
- name: Fail if Docker is not available
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- docker_check.rc == 0
|
||||||
|
fail_msg: >
|
||||||
|
Docker is not installed or not in PATH.
|
||||||
|
Please install Docker before running this playbook.
|
||||||
|
|
||||||
|
# ── 2. Download forgejo-runner binary ──────────────────────────────
|
||||||
|
- name: Download forgejo-runner binary
|
||||||
|
get_url:
|
||||||
|
url: "{{ forgejo_runner_url }}"
|
||||||
|
dest: "{{ forgejo_runner_bin_path }}"
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
# ── 3. Create runner system user ───────────────────────────────────
|
||||||
|
- name: Create runner system user
|
||||||
|
user:
|
||||||
|
name: "{{ forgejo_runner_user }}"
|
||||||
|
system: yes
|
||||||
|
shell: /usr/sbin/nologin
|
||||||
|
home: "{{ forgejo_runner_dir }}"
|
||||||
|
create_home: no
|
||||||
|
groups: docker
|
||||||
|
append: yes
|
||||||
|
comment: 'Forgejo Runner'
|
||||||
|
|
||||||
|
# ── 4. Create working directory ────────────────────────────────────
|
||||||
|
- name: Create forgejo-runner working directory
|
||||||
|
file:
|
||||||
|
path: "{{ forgejo_runner_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: "{{ forgejo_runner_user }}"
|
||||||
|
group: "{{ forgejo_runner_user }}"
|
||||||
|
mode: '0750'
|
||||||
|
|
||||||
|
# ── 5. Generate default config ─────────────────────────────────────
|
||||||
|
- name: Check if config already exists
|
||||||
|
stat:
|
||||||
|
path: "{{ forgejo_runner_config_path }}"
|
||||||
|
register: config_stat
|
||||||
|
|
||||||
|
- name: Generate default config
|
||||||
|
shell: "{{ forgejo_runner_bin_path }} generate-config > {{ forgejo_runner_config_path }}"
|
||||||
|
args:
|
||||||
|
chdir: "{{ forgejo_runner_dir }}"
|
||||||
|
when: not config_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Set config file ownership
|
||||||
|
file:
|
||||||
|
path: "{{ forgejo_runner_config_path }}"
|
||||||
|
owner: "{{ forgejo_runner_user }}"
|
||||||
|
group: "{{ forgejo_runner_user }}"
|
||||||
|
when: not config_stat.stat.exists
|
||||||
|
|
||||||
|
# ── 6. Register runner ─────────────────────────────────────────────
|
||||||
|
- name: Check if runner is already registered
|
||||||
|
stat:
|
||||||
|
path: "{{ forgejo_runner_dir }}/.runner"
|
||||||
|
register: runner_stat
|
||||||
|
|
||||||
|
- name: Register runner with Forgejo instance
|
||||||
|
command: >
|
||||||
|
{{ forgejo_runner_bin_path }} register --no-interactive
|
||||||
|
--instance {{ forgejo_instance_url }}
|
||||||
|
--token {{ forgejo_runner_registration_token }}
|
||||||
|
--name forgejo-runner-box
|
||||||
|
--labels "{{ forgejo_runner_labels }}"
|
||||||
|
args:
|
||||||
|
chdir: "{{ forgejo_runner_dir }}"
|
||||||
|
when: not runner_stat.stat.exists
|
||||||
|
|
||||||
|
- name: Set runner registration file ownership
|
||||||
|
file:
|
||||||
|
path: "{{ forgejo_runner_dir }}/.runner"
|
||||||
|
owner: "{{ forgejo_runner_user }}"
|
||||||
|
group: "{{ forgejo_runner_user }}"
|
||||||
|
when: not runner_stat.stat.exists
|
||||||
|
|
||||||
|
# ── 7. Create systemd service ──────────────────────────────────────
|
||||||
|
- name: Create forgejo-runner systemd service
|
||||||
|
copy:
|
||||||
|
dest: /etc/systemd/system/forgejo-runner.service
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Forgejo Runner
|
||||||
|
Documentation=https://forgejo.org/docs/latest/admin/actions/
|
||||||
|
After=docker.service
|
||||||
|
Requires=docker.service
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ forgejo_runner_user }}
|
||||||
|
Group={{ forgejo_runner_user }}
|
||||||
|
WorkingDirectory={{ forgejo_runner_dir }}
|
||||||
|
ExecStart={{ forgejo_runner_bin_path }} daemon --config {{ forgejo_runner_config_path }}
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
# ── 8. Reload systemd, enable and start ────────────────────────────
|
||||||
|
- name: Reload systemd
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: Enable and start forgejo-runner service
|
||||||
|
systemd:
|
||||||
|
name: forgejo-runner
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
|
||||||
|
# ── 9. Verify runner is active ─────────────────────────────────────
|
||||||
|
- name: Verify forgejo-runner is active
|
||||||
|
command: systemctl is-active forgejo-runner
|
||||||
|
register: runner_active
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Assert runner is running
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- runner_active.stdout == "active"
|
||||||
|
fail_msg: "forgejo-runner service is not active: {{ runner_active.stdout }}"
|
||||||
|
|
||||||
|
# ── 10. Set up Uptime Kuma push monitor ────────────────────────────
|
||||||
|
- name: Create Uptime Kuma push monitor setup script
|
||||||
|
copy:
|
||||||
|
dest: /tmp/setup_forgejo_runner_monitor.py
|
||||||
|
content: |
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import json
|
||||||
|
from uptime_kuma_api import UptimeKumaApi
|
||||||
|
|
||||||
|
def main():
|
||||||
|
api_url = sys.argv[1]
|
||||||
|
username = sys.argv[2]
|
||||||
|
password = sys.argv[3]
|
||||||
|
group_name = sys.argv[4]
|
||||||
|
monitor_name = sys.argv[5]
|
||||||
|
monitor_description = sys.argv[6]
|
||||||
|
interval = int(sys.argv[7])
|
||||||
|
retries = int(sys.argv[8])
|
||||||
|
ntfy_topic = sys.argv[9] if len(sys.argv) > 9 else "alerts"
|
||||||
|
|
||||||
|
api = UptimeKumaApi(api_url, timeout=60, wait_events=2.0)
|
||||||
|
api.login(username, password)
|
||||||
|
|
||||||
|
# Get all monitors
|
||||||
|
monitors = api.get_monitors()
|
||||||
|
|
||||||
|
# Get all notifications and find ntfy notification
|
||||||
|
notifications = api.get_notifications()
|
||||||
|
ntfy_notification = next((n for n in notifications if n.get('name') == f'ntfy ({ntfy_topic})'), None)
|
||||||
|
notification_id_list = {}
|
||||||
|
if ntfy_notification:
|
||||||
|
notification_id_list[ntfy_notification['id']] = True
|
||||||
|
|
||||||
|
# Find or create group
|
||||||
|
group = next((m for m in monitors if m.get('name') == group_name and m.get('type') == 'group'), None)
|
||||||
|
if not group:
|
||||||
|
group_result = api.add_monitor(type='group', name=group_name)
|
||||||
|
# Refresh to get the full group object with id
|
||||||
|
monitors = api.get_monitors()
|
||||||
|
group = next((m for m in monitors if m.get('name') == group_name and m.get('type') == 'group'), None)
|
||||||
|
|
||||||
|
# Find or create/update push monitor
|
||||||
|
existing_monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||||
|
|
||||||
|
monitor_data = {
|
||||||
|
'type': 'push',
|
||||||
|
'name': monitor_name,
|
||||||
|
'parent': group['id'],
|
||||||
|
'interval': interval,
|
||||||
|
'upsideDown': False,
|
||||||
|
'maxretries': retries,
|
||||||
|
'description': monitor_description,
|
||||||
|
'notificationIDList': notification_id_list
|
||||||
|
}
|
||||||
|
|
||||||
|
if existing_monitor:
|
||||||
|
monitor = api.edit_monitor(existing_monitor['id'], **monitor_data)
|
||||||
|
# Refresh to get the full monitor object with pushToken
|
||||||
|
monitors = api.get_monitors()
|
||||||
|
monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||||
|
else:
|
||||||
|
monitor_result = api.add_monitor(**monitor_data)
|
||||||
|
# Refresh to get the full monitor object with pushToken
|
||||||
|
monitors = api.get_monitors()
|
||||||
|
monitor = next((m for m in monitors if m.get('name') == monitor_name), None)
|
||||||
|
|
||||||
|
result = {
|
||||||
|
'monitor_id': monitor['id'],
|
||||||
|
'push_token': monitor['pushToken'],
|
||||||
|
'group_name': group_name,
|
||||||
|
'group_id': group['id'],
|
||||||
|
'monitor_name': monitor_name
|
||||||
|
}
|
||||||
|
print(json.dumps(result))
|
||||||
|
|
||||||
|
api.disconnect()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
mode: '0755'
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
|
||||||
|
- name: Run Uptime Kuma push monitor setup
|
||||||
|
command: >
|
||||||
|
{{ ansible_playbook_python }}
|
||||||
|
/tmp/setup_forgejo_runner_monitor.py
|
||||||
|
"{{ uptime_kuma_api_url }}"
|
||||||
|
"{{ uptime_kuma_username }}"
|
||||||
|
"{{ uptime_kuma_password }}"
|
||||||
|
"services"
|
||||||
|
"forgejo-runner-healthcheck"
|
||||||
|
"Forgejo Runner healthcheck - ping every {{ healthcheck_interval_seconds }}s"
|
||||||
|
"{{ healthcheck_timeout_seconds }}"
|
||||||
|
"{{ healthcheck_retries }}"
|
||||||
|
"{{ ntfy_topic }}"
|
||||||
|
register: monitor_setup_result
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Parse monitor setup result
|
||||||
|
set_fact:
|
||||||
|
monitor_info_parsed: "{{ monitor_setup_result.stdout | from_json }}"
|
||||||
|
|
||||||
|
- name: Set push URL
|
||||||
|
set_fact:
|
||||||
|
uptime_kuma_push_url: "{{ uptime_kuma_api_url }}/api/push/{{ monitor_info_parsed.push_token }}"
|
||||||
|
|
||||||
|
- name: Create healthcheck script directory
|
||||||
|
file:
|
||||||
|
path: "{{ healthcheck_script_dir }}"
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Create forgejo-runner healthcheck script
|
||||||
|
copy:
|
||||||
|
dest: "{{ healthcheck_script_path }}"
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Forgejo Runner Healthcheck Script
|
||||||
|
# Checks if forgejo-runner is active and pings Uptime Kuma on success
|
||||||
|
|
||||||
|
LOG_FILE="{{ healthcheck_log_file }}"
|
||||||
|
UPTIME_KUMA_URL="{{ uptime_kuma_push_url }}"
|
||||||
|
|
||||||
|
log_message() {
|
||||||
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_FILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
if systemctl is-active --quiet forgejo-runner; then
|
||||||
|
log_message "forgejo-runner is active, sending ping"
|
||||||
|
response=$(curl -s -w "\n%{http_code}" "$UPTIME_KUMA_URL?status=up&msg=forgejo-runner%20is%20active" 2>&1)
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
||||||
|
log_message "Ping sent successfully (HTTP $http_code)"
|
||||||
|
else
|
||||||
|
log_message "ERROR: Failed to send ping (HTTP $http_code)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_message "ERROR: forgejo-runner is not active"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0755'
|
||||||
|
|
||||||
|
- name: Create healthcheck systemd service
|
||||||
|
copy:
|
||||||
|
dest: "/etc/systemd/system/{{ healthcheck_service_name }}.service"
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Forgejo Runner Healthcheck
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=oneshot
|
||||||
|
ExecStart={{ healthcheck_script_path }}
|
||||||
|
User=root
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Create healthcheck systemd timer
|
||||||
|
copy:
|
||||||
|
dest: "/etc/systemd/system/{{ healthcheck_service_name }}.timer"
|
||||||
|
content: |
|
||||||
|
[Unit]
|
||||||
|
Description=Run Forgejo Runner Healthcheck every minute
|
||||||
|
Requires={{ healthcheck_service_name }}.service
|
||||||
|
|
||||||
|
[Timer]
|
||||||
|
OnBootSec=30sec
|
||||||
|
OnUnitActiveSec={{ healthcheck_interval_seconds }}sec
|
||||||
|
Persistent=true
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=timers.target
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: '0644'
|
||||||
|
|
||||||
|
- name: Reload systemd for healthcheck units
|
||||||
|
systemd:
|
||||||
|
daemon_reload: yes
|
||||||
|
|
||||||
|
- name: Enable and start healthcheck timer
|
||||||
|
systemd:
|
||||||
|
name: "{{ healthcheck_service_name }}.timer"
|
||||||
|
enabled: yes
|
||||||
|
state: started
|
||||||
|
|
||||||
|
- name: Test healthcheck script
|
||||||
|
command: "{{ healthcheck_script_path }}"
|
||||||
|
register: healthcheck_test
|
||||||
|
changed_when: false
|
||||||
|
|
||||||
|
- name: Verify healthcheck script works
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- healthcheck_test.rc == 0
|
||||||
|
fail_msg: "Healthcheck script failed to execute properly"
|
||||||
|
|
||||||
|
- name: Display deployment summary
|
||||||
|
debug:
|
||||||
|
msg: |
|
||||||
|
Forgejo Runner deployed successfully!
|
||||||
|
|
||||||
|
Runner Name: forgejo-runner-box
|
||||||
|
Instance: {{ forgejo_instance_url }}
|
||||||
|
Working Directory: {{ forgejo_runner_dir }}
|
||||||
|
Service: forgejo-runner.service ({{ runner_active.stdout }})
|
||||||
|
|
||||||
|
Healthcheck Monitor: forgejo-runner-healthcheck
|
||||||
|
Healthcheck Interval: Every {{ healthcheck_interval_seconds }}s
|
||||||
|
Timeout: {{ healthcheck_timeout_seconds }}s
|
||||||
|
|
||||||
|
- name: Clean up temporary monitor setup script
|
||||||
|
file:
|
||||||
|
path: /tmp/setup_forgejo_runner_monitor.py
|
||||||
|
state: absent
|
||||||
|
delegate_to: localhost
|
||||||
|
become: no
|
||||||
9
ansible/services/forgejo-runner/forgejo_runner_vars.yml
Normal file
9
ansible/services/forgejo-runner/forgejo_runner_vars.yml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
forgejo_runner_version: "6.3.1"
|
||||||
|
forgejo_runner_arch: "linux-amd64"
|
||||||
|
forgejo_runner_url: "https://code.forgejo.org/forgejo/runner/releases/download/v{{ forgejo_runner_version }}/forgejo-runner-{{ forgejo_runner_version }}-{{ forgejo_runner_arch }}"
|
||||||
|
forgejo_runner_bin_path: "/usr/local/bin/forgejo-runner"
|
||||||
|
forgejo_runner_user: "runner"
|
||||||
|
forgejo_runner_dir: "/opt/forgejo-runner"
|
||||||
|
forgejo_runner_config_path: "{{ forgejo_runner_dir }}/config.yml"
|
||||||
|
forgejo_runner_labels: "docker:docker://node:20-bookworm,ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://node:20-bookworm,ubuntu-24.04:docker://node:20-bookworm"
|
||||||
|
forgejo_instance_url: "https://forgejo.contrapeso.xyz"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue