Compare commits
5 Commits
d868ab2c3c
...
1b0c289b9b
| Author | SHA256 | Date | |
|---|---|---|---|
| 1b0c289b9b | |||
| 44261c95fb | |||
| 4436599693 | |||
| b5e082e400 | |||
| 62c413d42d |
2
.ansible-lint
Normal file
2
.ansible-lint
Normal file
@@ -0,0 +1,2 @@
|
||||
skip_list:
|
||||
- yaml[line-length]
|
||||
@@ -4,5 +4,5 @@
|
||||
|
||||
python -m ensurepip --upgrade
|
||||
pip install --upgrade pip
|
||||
pip install --upgrade --requirement requirements "$@"
|
||||
pip install --upgrade -r requirements "$@"
|
||||
ansible-galaxy install --role-file collections/requirements.yml "$@"
|
||||
19
group_vars/laptop/dotfiles.yml
Normal file
19
group_vars/laptop/dotfiles.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Home dotfile packages to deploy
|
||||
dotfiles_home_packages:
|
||||
- aichat
|
||||
- btrbk
|
||||
- env
|
||||
- git
|
||||
- nautilus
|
||||
- ssh
|
||||
- tmux
|
||||
- vim
|
||||
- x2go
|
||||
- zsh
|
||||
|
||||
# Root dotfile packages to deploy
|
||||
dotfiles_root_packages:
|
||||
- dnf
|
||||
- sysconfig
|
||||
- keyd
|
||||
19
group_vars/workstation/dotfiles.yml
Normal file
19
group_vars/workstation/dotfiles.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
---
|
||||
# Home dotfile packages to deploy
|
||||
dotfiles_home_packages:
|
||||
- aichat
|
||||
- btrbk-ws
|
||||
- env
|
||||
- git
|
||||
- nautilus
|
||||
- ssh
|
||||
- tmux
|
||||
- vim
|
||||
- x2go
|
||||
- zsh
|
||||
|
||||
# Root dotfile packages to deploy
|
||||
dotfiles_root_packages:
|
||||
- dnf
|
||||
- sysconfig
|
||||
- pwrstatd
|
||||
48
group_vars/workstation/quadlets.yml
Normal file
48
group_vars/workstation/quadlets.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
---
|
||||
|
||||
debug: false # set to true to print debug info
|
||||
log_level: "info"
|
||||
quadlets_deploy_configs: true # deploy quadlet service configs
|
||||
|
||||
maintainer: "Bryan C. Roessler"
|
||||
tz: "America/New_York"
|
||||
alpine_base_image: "docker.io/alpine:3.23"
|
||||
alpine_mirror: "https://mirror.pilotfiber.com/alpinelinux"
|
||||
|
||||
# Directories
|
||||
config_root: "{{ lookup('env', 'HOME') }}/.config"
|
||||
download_root: "{{ lookup('env', 'HOME') }}/downloads"
|
||||
monitor_root: "{{ lookup('env', 'HOME') }}/downloads"
|
||||
media_root: "{{ lookup('env', 'HOME') }}/media"
|
||||
|
||||
# linux-system-roles.podman
|
||||
podman_create_host_directories: true
|
||||
podman_host_directories:
|
||||
DEFAULT:
|
||||
owner: "{{ lookup('env', 'USER') }}"
|
||||
group: "{{ lookup('env', 'USER') }}"
|
||||
mode: "0755"
|
||||
podman_activate_systemd_units: true
|
||||
podman_run_as_user: "{{ lookup('env', 'USER') }}"
|
||||
podman_run_as_group: "{{ lookup('env', 'USER') }}"
|
||||
podman_systemd_unit_scope: user
|
||||
podman_prune_images: true
|
||||
podman_pull_image: false
|
||||
podman_continue_if_pull_fails: false
|
||||
|
||||
# Pod definitions
|
||||
pods:
|
||||
- name: htpc
|
||||
network: pasta
|
||||
publish:
|
||||
- "8080:80"
|
||||
- "8443:443"
|
||||
containers:
|
||||
- sabnzbd
|
||||
- qbittorrent
|
||||
- sonarr
|
||||
- radarr
|
||||
- lidarr
|
||||
- prowlarr
|
||||
- unpackerr
|
||||
- traefik
|
||||
@@ -35,6 +35,12 @@
|
||||
- role: services
|
||||
tags: ['services']
|
||||
|
||||
- name: Deploy quadlets
|
||||
hosts: all
|
||||
roles:
|
||||
- role: quadlets
|
||||
tags: ['quadlets']
|
||||
|
||||
- name: Deploy sysconfig
|
||||
hosts: all
|
||||
roles:
|
||||
|
||||
54
roles/deploy_files.yml
Normal file
54
roles/deploy_files.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
---
|
||||
# Reusable task to deploy files and templates from role paths
|
||||
# Parameters:
|
||||
# - subdir: subdirectory under role/<group>/ (e.g., 'home', 'root') (optional, default: '')
|
||||
# - target_root: target root path (e.g., ansible_facts.env.HOME, '/etc')
|
||||
# - use_symlinks: bool, whether to use symlinks for local deployments (default: true)
|
||||
# - become_root: bool, whether to become root (default: false)
|
||||
|
||||
- name: "Build file lists for {{ var_prefix }}"
|
||||
ansible.builtin.set_fact:
|
||||
"{{ var_prefix }}_files": "{{ lookup('community.general.filetree',
|
||||
role_path ~ '/' ~ (subdir | default('')), wantlist=True) | selectattr('state', 'equalto', 'file') | rejectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
"{{ var_prefix }}_templates": "{{ lookup('community.general.filetree',
|
||||
role_path ~ '/' ~ (subdir | default('')), wantlist=True) | selectattr('state', 'equalto', 'file') | selectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
|
||||
- name: "Ensure directories exist for {{ var_prefix }}"
|
||||
ansible.builtin.file:
|
||||
path: "{{ target_root }}/{{ item.path | dirname }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop: "{{ lookup('vars', var_prefix ~ '_files') + lookup('vars', var_prefix ~ '_templates') }}"
|
||||
when: item.path | dirname != ''
|
||||
become: "{{ become_root | default(false) }}"
|
||||
|
||||
- name: "Deploy files (local with symlinks) for {{ var_prefix }}"
|
||||
ansible.builtin.file:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ target_root }}/{{ item.path }}"
|
||||
state: link
|
||||
force: true
|
||||
loop: "{{ lookup('vars', var_prefix ~ '_files') }}"
|
||||
when:
|
||||
- ansible_connection in ['local', 'localhost']
|
||||
- use_symlinks | default(true)
|
||||
become: "{{ become_root | default(false) }}"
|
||||
|
||||
- name: "Deploy files (local/remote copy) for {{ var_prefix }}"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ target_root }}/{{ item.path }}"
|
||||
mode: preserve
|
||||
loop: "{{ lookup('vars', var_prefix ~ '_files') }}"
|
||||
when: |
|
||||
(ansible_connection not in ['local', 'localhost']) or
|
||||
(not (use_symlinks | default(true)))
|
||||
become: "{{ become_root | default(false) }}"
|
||||
|
||||
- name: "Render templates for {{ var_prefix }}"
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ target_root }}/{{ item.path | replace('.j2', '') }}"
|
||||
mode: preserve
|
||||
loop: "{{ lookup('vars', var_prefix ~ '_templates') }}"
|
||||
become: "{{ become_root | default(false) }}"
|
||||
@@ -1,82 +1,20 @@
|
||||
---
|
||||
- name: Build file lists
|
||||
ansible.builtin.set_fact:
|
||||
dotfiles_files_home: "{{ lookup('community.general.filetree',
|
||||
playbook_dir ~ '/dotfiles/common/home',
|
||||
playbook_dir ~ '/dotfiles/' ~ group_names[0] ~ '/home') | selectattr('state', 'equalto', 'file') | rejectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
dotfiles_templates_home: "{{ lookup('community.general.filetree',
|
||||
playbook_dir ~ '/dotfiles/common/home',
|
||||
playbook_dir ~ '/dotfiles/' ~ group_names[0] ~ '/home') | selectattr('state', 'equalto', 'file') | selectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
dotfiles_files_root: "{{ lookup('community.general.filetree',
|
||||
playbook_dir ~ '/dotfiles/common/root',
|
||||
playbook_dir ~ '/dotfiles/' ~ group_names[0] ~ '/root') | selectattr('state', 'equalto', 'file') | rejectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
dotfiles_templates_root: "{{ lookup('community.general.filetree',
|
||||
playbook_dir ~ '/dotfiles/common/root',
|
||||
playbook_dir ~ '/dotfiles/' ~ group_names[0] ~ '/root') | selectattr('state', 'equalto', 'file') | selectattr('path', 'match', '.*\\.j2$') | list }}"
|
||||
- name: Deploy selected home dotfile packages
|
||||
ansible.builtin.include_tasks: ../../deploy_files.yml
|
||||
vars:
|
||||
var_prefix: dotfiles_home
|
||||
subdir: "home/{{ item }}"
|
||||
target_root: "{{ ansible_facts.env.HOME }}"
|
||||
use_symlinks: true
|
||||
become_root: false
|
||||
loop: "{{ dotfiles_home_packages | default([]) }}"
|
||||
|
||||
- name: Ensure home directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ ansible_facts.env.HOME }}/{{ item.path | dirname }}"
|
||||
state: directory
|
||||
mode: "{{ '0700' if (item.path | dirname).startswith('.ssh') else '0755' }}"
|
||||
loop: "{{ dotfiles_files_home + dotfiles_templates_home }}"
|
||||
when: item.path | dirname != ''
|
||||
|
||||
- name: Deploy home files (local)
|
||||
ansible.builtin.file:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ ansible_facts.env.HOME }}/{{ item.path }}"
|
||||
state: link
|
||||
force: true
|
||||
loop: "{{ dotfiles_files_home }}"
|
||||
when: ansible_connection in ['local', 'localhost']
|
||||
|
||||
- name: Deploy home files (remote)
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ ansible_facts.env.HOME }}/{{ item.path }}"
|
||||
mode: preserve
|
||||
loop: "{{ dotfiles_files_home }}"
|
||||
when: ansible_connection not in ['local', 'localhost']
|
||||
|
||||
- name: Render home templates
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ ansible_facts.env.HOME }}/{{ item.path | replace('.j2', '') }}"
|
||||
mode: preserve
|
||||
loop: "{{ dotfiles_templates_home }}"
|
||||
|
||||
- name: Ensure root directories
|
||||
ansible.builtin.file:
|
||||
path: "/{{ item.path | dirname }}"
|
||||
state: directory
|
||||
mode: '0755'
|
||||
loop: "{{ dotfiles_files_root + dotfiles_templates_root }}"
|
||||
become: true
|
||||
|
||||
- name: Deploy root files (local)
|
||||
ansible.builtin.file:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/{{ item.path }}"
|
||||
state: link
|
||||
force: true
|
||||
loop: "{{ dotfiles_files_root }}"
|
||||
when: ansible_connection in ['local', 'localhost']
|
||||
become: true
|
||||
|
||||
- name: Deploy root files (remote)
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/{{ item.path }}"
|
||||
mode: preserve
|
||||
loop: "{{ dotfiles_files_root }}"
|
||||
when: ansible_connection not in ['local', 'localhost']
|
||||
become: true
|
||||
|
||||
- name: Render root templates
|
||||
ansible.builtin.template:
|
||||
src: "{{ item.src }}"
|
||||
dest: "/{{ item.path | replace('.j2', '') }}"
|
||||
mode: preserve
|
||||
loop: "{{ dotfiles_templates_root }}"
|
||||
become: true
|
||||
- name: Deploy selected root dotfile packages
|
||||
ansible.builtin.include_tasks: ../../deploy_files.yml
|
||||
vars:
|
||||
var_prefix: dotfiles_root
|
||||
subdir: "root/{{ item }}"
|
||||
target_root: "/"
|
||||
use_symlinks: true
|
||||
become_root: true
|
||||
loop: "{{ dotfiles_root_packages | default([]) }}"
|
||||
|
||||
0
roles/quadlets/README.md
Normal file
0
roles/quadlets/README.md
Normal file
11
roles/quadlets/lazylibrarian/quadlets.yml
Normal file
11
roles/quadlets/lazylibrarian/quadlets.yml
Normal file
@@ -0,0 +1,11 @@
|
||||
- name: lazylibrarian
|
||||
type: container
|
||||
image: localhost/lazylibrarian:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/lazylibrarian:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ media_root }}/ebooks:/ebooks:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
command: ["/venv/bin/python", "/app/LazyLibrarian.py", "--nolaunch", "--port", "{{ lazylibrarian_port | default('5299') }}", "--datadir=/config"]
|
||||
restart_policy: on-failure
|
||||
48
roles/quadlets/lidarr/quadlets.yml
Normal file
48
roles/quadlets/lidarr/quadlets.yml
Normal file
@@ -0,0 +1,48 @@
|
||||
- name: lidarr
|
||||
type: build
|
||||
image: localhost/lidarr:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
icu-libs libintl sqlite-libs curl libmediainfo xmlstarlet jq \
|
||||
&& url=$(curl -sL "https://api.github.com/repos/Lidarr/Lidarr/releases/latest" \
|
||||
| jq -r '.assets[] | select(.name | test("linux-musl-core-x64.tar.gz$")) | .browser_download_url') \
|
||||
&& [ -n "$url" ] \
|
||||
&& curl -L -o /tmp/Lidarr.tar.gz "$url" \
|
||||
&& mkdir -p /app && tar -xzf /tmp/Lidarr.tar.gz --strip-components=1 -C /app \
|
||||
&& chmod +x /app/Lidarr \
|
||||
&& rm /tmp/Lidarr.tar.gz
|
||||
WORKDIR /app
|
||||
|
||||
- name: lidarr
|
||||
type: container
|
||||
image: localhost/lidarr:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/lidarr:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ media_root }}/music:/music:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
LIDARR__APP__LAUNCHBROWSER: false
|
||||
LIDARR__APP__INSTANCENAME: "{{ LIDARR__APP__INSTANCENAME | default('Lidarr') }}"
|
||||
LIDARR__AUTH__METHOD: "{{ LIDARR__AUTH__METHOD | default('Forms') }}"
|
||||
LIDARR__AUTH__APIKEY: "{{ LIDARR__AUTH__APIKEY | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits')) }}"
|
||||
LIDARR__AUTH__ENABLED: "{{ LIDARR__AUTH__ENABLED | default(true) }}"
|
||||
LIDARR__AUTH__REQUIRED: "{{ LIDARR__AUTH__REQUIRED | default('DisabledForLocalAddresses') }}"
|
||||
LIDARR__SERVER__ENABLESSL: "{{ LIDARR__SERVER__ENABLESSL | default(false) }}"
|
||||
LIDARR__SERVER__PORT: "{{ LIDARR__SERVER__PORT | default('8686') }}"
|
||||
LIDARR__SERVER__SSLPORT: "{{ LIDARR__SERVER__SSLPORT | default('6868') }}"
|
||||
LIDARR__SERVER__BINDADDRESS: "{{ LIDARR__SERVER__BINDADDRESS | default('*') }}"
|
||||
LIDARR__SERVER__SSLCERTPATH: "{{ LIDARR__SERVER__SSLCERTPATH | default('/config/ssl/server.crt') }}"
|
||||
LIDARR__SERVER__SSLCERTPASSWORD: "{{ LIDARR__SERVER__SSLCERTPASSWORD | default('/config/ssl/server.key') }}"
|
||||
LIDARR__LOG__ANALYTICSENABLED: "{{ LIDARR__LOG__ANALYTICSENABLED | default(false) }}"
|
||||
LIDARR__LOG__LEVEL: "{{ LIDARR__LOG__LEVEL | default('info') }}"
|
||||
LIDARR__UPDATE__AUTOMATICALLY: "{{ LIDARR__UPDATE__AUTOMATICALLY | default(false) }}"
|
||||
command: ["/app/Lidarr", "-nobrowser", "--data=/config"]
|
||||
restart_policy: on-failure
|
||||
44
roles/quadlets/prowlarr/quadlets.yml
Normal file
44
roles/quadlets/prowlarr/quadlets.yml
Normal file
@@ -0,0 +1,44 @@
|
||||
- name: prowlarr
|
||||
type: build
|
||||
image: localhost/prowlarr:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
libintl icu-libs sqlite-libs curl jq \
|
||||
&& url=$(curl -sL "https://api.github.com/repos/Prowlarr/Prowlarr/releases/latest" \
|
||||
| jq -r '.assets[] | select(.name | test("linux-musl-core-x64.tar.gz$")) | .browser_download_url') \
|
||||
&& [ -n "$url" ] \
|
||||
&& curl -L -o /tmp/Prowlarr.tar.gz "$url" \
|
||||
&& mkdir -p /app \
|
||||
&& tar -xzf /tmp/Prowlarr.tar.gz -C /app --strip-components=1 \
|
||||
&& rm /tmp/Prowlarr.tar.gz
|
||||
WORKDIR /app
|
||||
|
||||
- name: prowlarr
|
||||
type: container
|
||||
image: localhost/prowlarr:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/prowlarr:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
PROWLARR__APP__LAUNCHBROWSER: false
|
||||
PROWLARR__APP__INSTANCENAME: "{{ PROWLARR__APP__INSTANCENAME | default('Prowlarr') }}"
|
||||
PROWLARR__AUTH__METHOD: "{{ PROWLARR__AUTH__METHOD | default('Forms') }}"
|
||||
PROWLARR__AUTH__REQUIRED: "{{ PROWLARR__AUTH__REQUIRED | default('DisabledForLocalAddresses') }}"
|
||||
PROWLARR__AUTH__APIKEY: "{{ PROWLARR__AUTH__APIKEY | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits')) }}"
|
||||
PROWLARR__SERVER__ENABLESSL: "{{ PROWLARR__SERVER__ENABLESSL | default(false) }}"
|
||||
PROWLARR__SERVER__SSLPORT: "{{ PROWLARR__SERVER__PORT | default('6969') }}"
|
||||
PROWLARR__SERVER__PORT: "{{ PROWLARR__SERVER__PORT | default('9696') }}"
|
||||
PROWLARR__SERVER__BINDADDRESS: "{{ PROWLARR__SERVER__BINDADDRESS | default('*') }}"
|
||||
PROWLARR__LOG__ANALYTICSENABLED: "{{ PROWLARR__LOG__ANALYTICSENABLED | default(false) }}"
|
||||
PROWLARR__LOG__LEVEL: "{{ PROWLARR__LOG__LEVEL | default('info') }}"
|
||||
PROWLARR__UPDATE__AUTOMATICALLY: "{{ PROWLARR__UPDATE__AUTOMATICALLY | default(false) }}"
|
||||
command: ["/app/Prowlarr", "--nobrowser", "--data=/config"]
|
||||
restart_policy: on-failure
|
||||
30
roles/quadlets/qbittorrent/quadlets.yml
Normal file
30
roles/quadlets/qbittorrent/quadlets.yml
Normal file
@@ -0,0 +1,30 @@
|
||||
- name: qbittorrent
|
||||
type: build
|
||||
image: localhost/qbittorrent:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
qbittorrent-nox \
|
||||
&& mkdir -p /app \
|
||||
&& ln -s /usr/bin/qbittorrent-nox /app/qbittorrent-nox
|
||||
WORKDIR /app
|
||||
|
||||
- name: qbittorrent
|
||||
type: container
|
||||
image: localhost/qbittorrent:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/qbittorrent:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ monitor_root }}/torrents:/torrents:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
command:
|
||||
- "qbittorrent-nox"
|
||||
- "--profile=/config"
|
||||
restart_policy: on-failure
|
||||
46
roles/quadlets/radarr/quadlets.yml
Normal file
46
roles/quadlets/radarr/quadlets.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
|
||||
- name: radarr
|
||||
type: build
|
||||
image: localhost/radarr:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
icu-libs libintl sqlite-libs curl libmediainfo xmlstarlet jq \
|
||||
&& url=$(curl -sL "https://api.github.com/repos/Radarr/Radarr/releases/latest" \
|
||||
| jq -r '.assets[] | select(.name | test("linux-musl-core-x64.tar.gz$")) | .browser_download_url') \
|
||||
&& curl -L -o /tmp/radarr.tar.gz "$url" \
|
||||
&& mkdir -p /app \
|
||||
&& tar -xzf /tmp/radarr.tar.gz --strip-components=1 -C /app \
|
||||
&& chmod +x /app/Radarr \
|
||||
&& rm -rf /app/Radarr.Update
|
||||
WORKDIR /app
|
||||
|
||||
- name: radarr
|
||||
type: container
|
||||
image: localhost/radarr:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/radarr:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ media_root }}/movies:/movies:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
RADARR__APP__LAUNCHBROWSER: false
|
||||
RADARR__APP__INSTANCENAME: "{{ RADARR__APP__INSTANCENAME | default('Radarr') }}"
|
||||
RADARR__AUTH__METHOD: "{{ RADARR__AUTH__METHOD | default('Forms') }}"
|
||||
RADARR__AUTH__APIKEY: "{{ RADARR__AUTH__APIKEY | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits')) }}"
|
||||
RADARR__AUTH__ENABLED: "{{ RADARR__AUTH__ENABLED | default(true) }}"
|
||||
RADARR__AUTH__REQUIRED: "{{ RADARR__AUTH__REQUIRED | default('DisabledForLocalAddresses') }}"
|
||||
RADARR__SERVER__ENABLESSL: "{{ RADARR__SERVER__ENABLESSL | default(false) }}"
|
||||
RADARR__SERVER__PORT: "{{ RADARR__SERVER__PORT | default('7878') }}"
|
||||
RADARR__SERVER__BINDADDRESS: "{{ RADARR__SERVER__BINDADDRESS | default('*') }}"
|
||||
RADARR__LOG__ANALYTICSENABLED: "{{ RADARR__LOG__ANALYTICSENABLED | default(false) }}"
|
||||
RADARR__LOG__LEVEL: "{{ RADARR__LOG__LEVEL | default('info') }}"
|
||||
RADARR__UPDATE__AUTOMATICALLY: "{{ RADARR__UPDATE__AUTOMATICALLY | default(false) }}"
|
||||
command: ["/app/Radarr", "--nobrowser", "--data=/config"]
|
||||
restart_policy: on-failure
|
||||
42
roles/quadlets/sabnzbd/quadlets.yml
Normal file
42
roles/quadlets/sabnzbd/quadlets.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
- name: sabnzbd
|
||||
type: build
|
||||
image: localhost/sabnzbd:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN \
|
||||
rm -rf /etc/apk/repositories \
|
||||
&& apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
python3 py3-pip git unzip ca-certificates par2cmdline 7zip \
|
||||
&& apk add --no-cache --virtual=.build-deps \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
autoconf automake build-base openssl-dev libffi-dev python3-dev \
|
||||
&& apk add --no-cache -X '{{ alpine_mirror }}/v3.14/main' unrar \
|
||||
&& git clone --depth 1 --branch master https://github.com/sabnzbd/sabnzbd /app \
|
||||
&& python3 -m venv /venv \
|
||||
&& . /venv/bin/activate \
|
||||
&& pip install --no-cache-dir -r /app/requirements.txt \
|
||||
&& apk del --purge .build-deps
|
||||
|
||||
- name: sabnzbd
|
||||
type: container
|
||||
image: localhost/sabnzbd:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/sabnzbd:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ monitor_root }}/nzbs:/nzbs:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
command:
|
||||
- "/venv/bin/python"
|
||||
- "-OO"
|
||||
- "/app/SABnzbd.py"
|
||||
- "--config-file=/config/sabnzbd.ini"
|
||||
- "--browser=0"
|
||||
restart_policy: on-failure
|
||||
43
roles/quadlets/sonarr/quadlets.yml
Normal file
43
roles/quadlets/sonarr/quadlets.yml
Normal file
@@ -0,0 +1,43 @@
|
||||
- name: sonarr
|
||||
type: build
|
||||
image: localhost/sonarr:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
libintl sqlite-libs icu-libs curl jq \
|
||||
&& url=$(curl -sL "https://api.github.com/repos/Sonarr/Sonarr/releases/latest" \
|
||||
| jq -r '.assets[] | select(.name | test("linux-musl-x64.tar.gz$")) | .browser_download_url') \
|
||||
&& curl -L -o /tmp/Sonarr.tar.gz "$url" \
|
||||
&& mkdir -p /app && tar -xzf /tmp/Sonarr.tar.gz -C /app --strip-components=1
|
||||
WORKDIR /app
|
||||
|
||||
- name: sonarr
|
||||
type: container
|
||||
image: localhost/sonarr:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/sonarr:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
- "{{ media_root }}/tv:/tv:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
SONARR__APP__LAUNCHBROWSER: false
|
||||
SONARR__APP__INSTANCENAME: "{{ SONARR__APP__INSTANCENAME | default('Sonarr') }}"
|
||||
SONARR__AUTH__METHOD: "{{ SONARR__AUTH__METHOD | default('Forms') }}"
|
||||
SONARR__AUTH__APIKEY: "{{ SONARR__AUTH__APIKEY | default(lookup('password', '/dev/null length=32 chars=ascii_letters,digits')) }}"
|
||||
SONARR__SERVER__ENABLESSL: "{{ SONARR__SERVER__ENABLESSL | default(false) }}"
|
||||
SONARR__SERVER__SSLPORT: "{{ SONARR__SERVER__SSLPORT | default('9898') }}"
|
||||
SONARR__SERVER__PORT: "{{ SONARR__SERVER__PORT | default('8989') }}"
|
||||
SONARR__SERVER__BINDADDRESS: "{{ SONARR__SERVER__BINDADDRESS | default('*') }}"
|
||||
SONARR__SERVER__SSLCERTPATH: "{{ SONARR__SERVER__SSLCERTPATH | default('/config/ssl/server.crt') }}"
|
||||
SONARR__SERVER__SSLCERTPASSWORD: "{{ SONARR__SERVER__SSLCERTPASSWORD | default('/config/ssl/server.key') }}"
|
||||
SONARR__LOG__ANALYTICSENABLED: "{{ SONARR__LOG__ANALYTICSENABLED | default(false) }}"
|
||||
SONARR__UPDATE__AUTOMATICALLY: "{{ SONARR__UPDATE__AUTOMATICALLY | default(false) }}"
|
||||
SONARR__LOG__LEVEL: "{{ SONARR__LOG__LEVEL | default('info') }}"
|
||||
command: ["/app/Sonarr", "-data=/config"]
|
||||
restart_policy: on-failure
|
||||
46
roles/quadlets/tasks/main.yml
Normal file
46
roles/quadlets/tasks/main.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
- name: Deploy quadlet app configs
|
||||
ansible.builtin.include_tasks: ../../deploy_files.yml
|
||||
vars:
|
||||
var_prefix: quadlets
|
||||
subdir: ''
|
||||
target_root: "{{ ansible_facts.env.HOME }}/.config"
|
||||
use_symlinks: false
|
||||
become_root: false
|
||||
when: quadlets_deploy_configs | default(false)
|
||||
|
||||
- name: Initialize quadlet specs
|
||||
ansible.builtin.set_fact:
|
||||
quadlets_all_specs: []
|
||||
|
||||
- name: Load all quadlet definitions
|
||||
ansible.builtin.set_fact:
|
||||
quadlets_all_specs: "{{ quadlets_all_specs + (lookup('file', item.path) | from_yaml) }}"
|
||||
loop: "{{ lookup('ansible.builtin.find', role_path, patterns='quadlets.yml', recurse=yes).files }}"
|
||||
|
||||
- name: Build final quadlet specs with pod injection
|
||||
ansible.builtin.set_fact:
|
||||
quadlets_specs: "{{ (pods | default([])) | map('combine', {'type': 'pod'}) | list }}"
|
||||
|
||||
- name: Add containers to quadlet specs with pod injection
|
||||
ansible.builtin.set_fact:
|
||||
quadlets_specs: "{{ quadlets_specs + [container_spec[0] | combine({'pod': item.0.name})] }}"
|
||||
loop: "{{ pods | default([]) | subelements('containers') }}"
|
||||
vars:
|
||||
container_spec: "{{ quadlets_all_specs | selectattr('name', 'equalto', item.1) | selectattr('type', 'equalto', 'container') | list }}"
|
||||
when: container_spec | length > 0
|
||||
|
||||
- name: Add build specs for enabled containers
|
||||
ansible.builtin.set_fact:
|
||||
quadlets_specs: "{{ quadlets_specs + [item] }}"
|
||||
loop: "{{ quadlets_all_specs }}"
|
||||
when:
|
||||
- item.type == 'build'
|
||||
- item.name in (pods | default([]) | map(attribute='containers') | flatten)
|
||||
|
||||
- name: Deploy quadlets using fedora.linux_system_roles.podman
|
||||
ansible.builtin.include_role:
|
||||
name: fedora.linux_system_roles.podman
|
||||
vars:
|
||||
podman_quadlet_specs: "{{ quadlets_specs }}"
|
||||
when: quadlets_specs is defined and quadlets_specs | length > 0
|
||||
19
roles/quadlets/traefik/quadlets.yml
Normal file
19
roles/quadlets/traefik/quadlets.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
- name: traefik
|
||||
type: container
|
||||
image: docker.io/traefik:latest
|
||||
pull: newer
|
||||
volumes:
|
||||
- "{{ config_root }}/traefik:/etc/traefik:Z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
command:
|
||||
- "--api.dashboard=true"
|
||||
- "--api.insecure=true"
|
||||
- "--providers.file.directory=/etc/traefik/dynamic"
|
||||
- "--providers.file.watch=true"
|
||||
- "--entrypoints.web.address=:80"
|
||||
- "--entrypoints.websecure.address=:443"
|
||||
- "--entrypoints.websecure.http.tls.certFile=/etc/traefik/server.crt"
|
||||
- "--entrypoints.websecure.http.tls.keyFile=/etc/traefik/server.key"
|
||||
- "--log.level=INFO"
|
||||
restart_policy: on-failure
|
||||
36
roles/quadlets/unpackerr/quadlets.yml
Normal file
36
roles/quadlets/unpackerr/quadlets.yml
Normal file
@@ -0,0 +1,36 @@
|
||||
- name: unpackerr
|
||||
type: build
|
||||
image: localhost/unpackerr:latest
|
||||
pull: missing
|
||||
format: oci
|
||||
force_rm: true
|
||||
container_file: |
|
||||
FROM {{ alpine_base_image }}
|
||||
LABEL maintainer="{{ maintainer }}"
|
||||
RUN apk add --no-cache \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/main" \
|
||||
-X "{{ alpine_mirror }}/v{{ alpine_base_image.split(':')[-1] }}/community" \
|
||||
curl tar jq \
|
||||
&& url=$(curl -sL "https://api.github.com/repos/davidnewhall/Unpackerr/releases/latest" \
|
||||
| jq -r '.assets[] | select(.name == "unpackerr.amd64.linux.gz") | .browser_download_url') \
|
||||
&& [ -n "$url" ] \
|
||||
&& curl -L -o /tmp/unpackerr.gz "$url" \
|
||||
&& mkdir -p /app \
|
||||
&& gunzip -c /tmp/unpackerr.gz > /app/unpackerr \
|
||||
&& chmod +x /app/unpackerr \
|
||||
&& rm /tmp/unpackerr.gz
|
||||
WORKDIR /app
|
||||
|
||||
- name: unpackerr
|
||||
type: container
|
||||
image: localhost/unpackerr:latest
|
||||
volumes:
|
||||
- "{{ config_root }}/unpackerr:/config:Z"
|
||||
- "{{ download_root }}/htpc:/downloads:z"
|
||||
env:
|
||||
TZ: "{{ tz }}"
|
||||
UNPACKERR__LOG__ANALYTICSENABLED: "{{ UNPACKERR__LOG__ANALYTICSENABLED | default(false) }}"
|
||||
UNPACKERR__UPDATE__AUTOMATICALLY: "{{ UNPACKERR__UPDATE__AUTOMATICALLY | default(false) }}"
|
||||
UNPACKERR__LOG__LEVEL: "{{ UNPACKERR__LOG__LEVEL | default('info') }}"
|
||||
command: ["/app/unpackerr"]
|
||||
restart_policy: on-failure
|
||||
@@ -1,25 +1,10 @@
|
||||
---
|
||||
|
||||
- name: Copy repo scripts to local bin (for remote hosts)
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ local_bin_dir | default(ansible_facts['env']['HOME'] ~ '/.local/bin') }}/{{ item | basename }}"
|
||||
mode: "0755"
|
||||
owner: "{{ local_bin_owner | default(ansible_facts['user_id']) }}"
|
||||
group: "{{ local_bin_group | default(ansible_facts['user_gid']) }}"
|
||||
with_fileglob:
|
||||
- "{{ scripts_src_glob | default(playbook_dir + '/scripts/*') }}"
|
||||
when: ansible_connection not in ['local', 'localhost'] and item is file
|
||||
|
||||
- name: Symlink repo scripts into local bin (stow-like, for local hosts)
|
||||
ansible.builtin.file:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ local_bin_dir | default(ansible_facts['env']['HOME'] ~ '/.local/bin') }}/{{ item | basename }}"
|
||||
state: link
|
||||
force: true
|
||||
owner: "{{ local_bin_owner | default(ansible_facts['user_id']) }}"
|
||||
group: "{{ local_bin_group | default(ansible_facts['user_gid']) }}"
|
||||
follow: false
|
||||
with_fileglob:
|
||||
- "{{ scripts_src_glob | default(playbook_dir + '/scripts/*') }}"
|
||||
when: ansible_connection in ['local', 'localhost'] and item is file
|
||||
- name: Deploy scripts to user's local bin
|
||||
ansible.builtin.include_tasks: ../../deploy_files.yml
|
||||
vars:
|
||||
var_prefix: scripts
|
||||
subdir: ''
|
||||
target_root: "{{ ansible_facts.env.HOME }}/.local/bin"
|
||||
use_symlinks: true
|
||||
become_root: false
|
||||
|
||||
Reference in New Issue
Block a user