diff --git a/.forgejo/workflows/ansible-ci.yml b/.forgejo/workflows/ansible-ci.yml
new file mode 100644
index 0000000..fceb974
--- /dev/null
+++ b/.forgejo/workflows/ansible-ci.yml
@@ -0,0 +1,35 @@
+---
+on:
+ - push
+
+jobs:
+ ansible-docsmith:
+ runs-on: node-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Install ansible-docsmith
+ run: |
+ apt-get update
+ apt-get install -y python3-pip
+ pip install ansible-docsmith --break-system-packages
+
+ - name: Run ansible-docsmith
+ run: ansible-docsmith generate .
+
+ - name: Verify changes
+ run: |
+ set -e
+ git diff > diff.txt
+ CHANGES=$(wc -l diff.txt | awk '{ print $1 }')
+ echo "Number of changes: ${CHANGES}"
+ if [ ${CHANGES} -gt 0 ] ; then
+ echo ""
+ cat diff.txt
+ echo ""
+ echo "Fix with the following command:"
+ echo ""
+ echo "run ansible-docsmith generate ${role_dir}"
+ exit 1
+ fi
diff --git a/README.md b/README.md
index 450baf0..9dc8a84 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
-# ansible
+# Ansible
-My Ansible collection
\ No newline at end of file
+My Ansible collection.
diff --git a/roles/certbot/tasks/main.yml b/roles/certbot/tasks/main.yml
new file mode 100644
index 0000000..b7241ef
--- /dev/null
+++ b/roles/certbot/tasks/main.yml
@@ -0,0 +1,17 @@
+---
+- name: Check requirements
+ ansible.builtin.assert:
+ that:
+ - certbot_email is defined
+ - certbot_domain is defined
+
+- name: Install packages
+ ansible.builtin.package:
+ name: certbot
+
+- name: Request certificate
+ ansible.builtin.command:
+ cmd: >-
+ certbot certonly --standalone -n --agree-tos
+ --email {{ certbot_email }} -d {{ certbot_domain }}
+ creates: /etc/letsencrypt/live/{{ certbot_domain }}/fullchain.pem
diff --git a/roles/coller/README.md b/roles/coller/README.md
new file mode 100644
index 0000000..5a5b2e0
--- /dev/null
+++ b/roles/coller/README.md
@@ -0,0 +1,43 @@
+# ansible-role-coller
+
+Ansible role to manage a [coller](https://git.riou.xyz/jriou/coller) instance.
+
+## Installation
+
+Clone the repository in your local Ansible roles directory:
+
+```
+git clone https://git.riou.xyz/jriou/ansible-role-coller.git ~/.ansible/roles/coller
+```
+
+See [Storing and finding
+roles](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_reuse_roles.html#storing-and-finding-roles).
+
+## Configuration
+
+See [Variable
+precedence](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#ansible-variable-precedence)
+to find where you should put your own variables.
+
+Then define at least `coller_db_password` with a strong and secure password,
+encrypted using
+[ansible-vault](https://docs.ansible.com/ansible/latest/cli/ansible-vault.html).
+
+See list of [default variables](defaults/main.yml).
+
+
+## Usage
+
+Example of a basic coller.yml playbook:
+
+```yaml
+- hosts: coller
+ roles:
+ - coller
+```
+
+Then run the playbook:
+
+```
+ansible-playbook coller.yml
+```
diff --git a/roles/coller/defaults/main.yml b/roles/coller/defaults/main.yml
new file mode 100644
index 0000000..59b7257
--- /dev/null
+++ b/roles/coller/defaults/main.yml
@@ -0,0 +1,9 @@
+---
+coller_version: 1.3.1
+coller_config_dir: /etc/coller
+coller_port: 8080
+coller_manage_iptables: false
+coller_allowed_sources: []
+coller_db_name: coller
+coller_db_user: coller
+#coller_db_password:
diff --git a/roles/coller/handlers/main.yml b/roles/coller/handlers/main.yml
new file mode 100644
index 0000000..aa0f296
--- /dev/null
+++ b/roles/coller/handlers/main.yml
@@ -0,0 +1,4 @@
+---
+- name: save iptables
+ ansible.builtin.shell:
+ cmd: netfilter-persistent save
diff --git a/roles/coller/meta/main.yml b/roles/coller/meta/main.yml
new file mode 100644
index 0000000..3f5647c
--- /dev/null
+++ b/roles/coller/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+- role: geerlingguy.docker
diff --git a/roles/coller/tasks/main.yml b/roles/coller/tasks/main.yml
new file mode 100644
index 0000000..e7141be
--- /dev/null
+++ b/roles/coller/tasks/main.yml
@@ -0,0 +1,55 @@
+---
+- name: check password
+ ansible.builtin.assert:
+ that:
+ - coller_db_password is defined
+
+- name: download source code
+ ansible.builtin.git:
+ repo: https://git.riou.xyz/jriou/coller.git
+ dest: /opt/coller
+ version: "{{ coller_version }}"
+
+- name: create directories
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: directory
+ owner: root
+ group: root
+ mode: "0755"
+ loop:
+ - "{{ coller_config_dir }}"
+
+- name: create docker-compose files
+ ansible.builtin.template:
+ src: "{{ item.src }}.j2"
+ dest: "{{ coller_config_dir }}/{{ item.src }}"
+ owner: root
+ group: root
+ mode: "{{ item.mode }}"
+ loop:
+ - src: docker-compose.yml
+ mode: "0644"
+ - src: db.env
+ mode: "0600"
+
+- name: create configuration file
+ ansible.builtin.copy:
+ content:
+ database_type: postgres
+ database_dsn: "host=db dbname={{ coller_db_name }} user={{ coller_db_user }} password={{ coller_db_password }}"
+ dest: "{{ coller_config_dir }}/collerd.json"
+ owner: root
+ group: root
+ mode: "0640"
+ no_log: true
+
+- name: start service
+ community.docker.docker_compose_v2:
+ project_src: "{{ coller_config_dir }}"
+ files:
+ - docker-compose.yml
+
+- name: manage iptables
+ when: coller_manage_iptables
+ ansible.builtin.include_tasks: manage-iptables.yml
diff --git a/roles/coller/tasks/manage-iptables.yml b/roles/coller/tasks/manage-iptables.yml
new file mode 100644
index 0000000..7626914
--- /dev/null
+++ b/roles/coller/tasks/manage-iptables.yml
@@ -0,0 +1,16 @@
+---
+- name: install packages
+ ansible.builtin.package:
+ name: netfilter-persistent
+
+- name: allow with iptables
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ source: "{{ item }}"
+ destination_ports:
+ - "{{ coller_port }}"
+ jump: ACCEPT
+ comment: coller
+ loop: "{{ coller_allowed_sources }}"
+ notify: save iptables
diff --git a/roles/coller/templates/db.env.j2 b/roles/coller/templates/db.env.j2
new file mode 100644
index 0000000..511cba4
--- /dev/null
+++ b/roles/coller/templates/db.env.j2
@@ -0,0 +1,6 @@
+{{ ansible_managed | comment }}
+POSTGRES_USER={{ coller_db_user }}
+POSTGRES_PASSWORD={{ coller_db_password }}
+POSTGRES_DB={{ coller_db_name }}
+POSTGRES_INITDB_ARGS="--data-checksums"
+POSTGRES_HOST_AUTH_METHOD=scram-sha-256
diff --git a/roles/coller/templates/docker-compose.yml.j2 b/roles/coller/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..1d46c35
--- /dev/null
+++ b/roles/coller/templates/docker-compose.yml.j2
@@ -0,0 +1,32 @@
+---
+{{ ansible_managed | comment }}
+services:
+ server:
+ image: coller:{{ coller_version }}
+ build: /opt/coller
+ container_name: collerd
+ restart: always
+ networks:
+ - coller
+ ports:
+ - "{{ coller_port }}:8080"
+ volumes:
+ - "{{ coller_config_dir }}/collerd.json:/etc/collerd.json:ro"
+ command: collerd -config /etc/collerd.json
+
+ db:
+ image: postgres:17
+ hostname: db
+ container_name: collerd_db
+ restart: always
+ env_file: {{ coller_config_dir }}/db.env
+ networks:
+ - coller
+ volumes:
+ - coller:/var/lib/postgresql/data
+
+networks:
+ coller:
+
+volumes:
+ coller:
diff --git a/roles/firefly/README.md b/roles/firefly/README.md
new file mode 100644
index 0000000..feadf6b
--- /dev/null
+++ b/roles/firefly/README.md
@@ -0,0 +1,39 @@
+# Ansible Role Firefly
+
+Ansible role to manage a [Firefly III](https://firefly-iii.org/) instance.
+
+## Configuration
+
+See [Variable
+precedence](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#ansible-variable-precedence)
+to find where you should put your own variables.
+
+Then define at least `firefly_static_cron_token`, `firefly_db_password` and
+`firefly_app_key` variables with a strong and secure password, encrypted using
+[ansible-vault](https://docs.ansible.com/ansible/latest/cli/ansible-vault.html).
+
+See list of [default variables](defaults/main.yml).
+
+
+## Usage
+
+Example of a basic firefly.yml playbook:
+
+```yaml
+hosts:
+ - firefly
+
+roles:
+ - firefly
+```
+
+Then run the playbook:
+
+```
+ansible-playbook firefly.yml
+```
+
+## Donate
+
+As we all love FOSS projects, you should consider [sponsoring and/or
+contribute](https://github.com/firefly-iii/firefly-iii).
diff --git a/roles/firefly/defaults/main.yml b/roles/firefly/defaults/main.yml
new file mode 100644
index 0000000..f01fe37
--- /dev/null
+++ b/roles/firefly/defaults/main.yml
@@ -0,0 +1,14 @@
+---
+firefly_version: latest
+firefly_port: 8080
+firefly_static_cron_token: CHANGEME
+firefly_home: /var/lib/firefly
+firefly_site_owner: root@localhost
+firefly_app_key: CHANGEME
+firefly_language: en_US
+firefly_tz: Etc/UTC
+firefly_db_database: firefly
+firefly_db_username: firefly
+firefly_db_password: CHANGEME
+firefly_manage_iptables: false
+firefly_allowed_sources: []
diff --git a/roles/firefly/handlers/main.yml b/roles/firefly/handlers/main.yml
new file mode 100644
index 0000000..aa0f296
--- /dev/null
+++ b/roles/firefly/handlers/main.yml
@@ -0,0 +1,4 @@
+---
+- name: save iptables
+ ansible.builtin.shell:
+ cmd: netfilter-persistent save
diff --git a/roles/firefly/meta/main.yml b/roles/firefly/meta/main.yml
new file mode 100644
index 0000000..3f5647c
--- /dev/null
+++ b/roles/firefly/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+- role: geerlingguy.docker
diff --git a/roles/firefly/tasks/main.yml b/roles/firefly/tasks/main.yml
new file mode 100644
index 0000000..65be46c
--- /dev/null
+++ b/roles/firefly/tasks/main.yml
@@ -0,0 +1,40 @@
+---
+- name: install dependencies
+ ansible.builtin.apt:
+ name:
+ - python3-docker
+ - python3-compose
+
+- name: create directories
+ ansible.builtin.file:
+ path: /etc/firefly
+ state: directory
+
+- name: create configuration files
+ ansible.builtin.template:
+ src: "{{ item }}.j2"
+ dest: "/etc/firefly/{{ item }}"
+ mode: "0600"
+ loop:
+ - docker-compose.yml
+ - db.env
+ - app.env
+
+- name: start service
+ community.docker.docker_compose_v2:
+ project_src: /etc/firefly
+ files:
+ - docker-compose.yml
+
+- name: allow with iptables
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ source: "{{ item }}"
+ destination_ports:
+ - "{{ firefly_port }}"
+ jump: ACCEPT
+ comment: firefly
+ loop: "{{ firefly_allowed_sources }}"
+ notify: save iptables
+ when: firefly_manage_iptables
diff --git a/roles/firefly/templates/app.env.j2 b/roles/firefly/templates/app.env.j2
new file mode 100644
index 0000000..89aa8aa
--- /dev/null
+++ b/roles/firefly/templates/app.env.j2
@@ -0,0 +1,132 @@
+APP_ENV=local
+APP_DEBUG=false
+
+SITE_OWNER={{ firefly_site_owner }}
+
+APP_KEY={{ firefly_app_key }}
+
+DEFAULT_LANGUAGE={{ firefly_language }}
+DEFAULT_LOCALE=equal
+
+TZ={{ firefly_tz }}
+
+TRUSTED_PROXIES=*
+
+LOG_CHANNEL=stack
+
+APP_LOG_LEVEL=notice
+
+AUDIT_LOG_LEVEL=emergency
+AUDIT_LOG_CHANNEL=
+PAPERTRAIL_HOST=
+PAPERTRAIL_PORT=
+
+# Database credentials. Make sure the database exists. I recommend a dedicated user for Firefly III
+# For other database types, please see the FAQ: https://docs.firefly-iii.org/firefly-iii/faq/self-hosted/#i-want-to-use-sqlite
+# If you use Docker or similar, you can set these variables from a file by appending them with _FILE
+# Use "pgsql" for PostgreSQL
+# Use "mysql" for MySQL and MariaDB.
+# Use "sqlite" for SQLite.
+DB_CONNECTION=pgsql
+DB_HOST=db
+DB_PORT=5432
+DB_DATABASE={{ firefly_db_database }}
+DB_USERNAME={{ firefly_db_username }}
+DB_PASSWORD={{ firefly_db_password }}
+DB_SOCKET=
+
+PGSQL_SSL_MODE=prefer
+PGSQL_SCHEMA=public
+
+CACHE_DRIVER=file
+SESSION_DRIVER=file
+
+REDIS_SCHEME=tcp
+REDIS_PATH=
+REDIS_HOST=redis
+REDIS_PORT=6379
+REDIS_USERNAME=firefly
+REDIS_PASSWORD=
+REDIS_DB="0"
+REDIS_CACHE_DB="1"
+
+COOKIE_PATH="/"
+COOKIE_DOMAIN=
+COOKIE_SECURE=false
+COOKIE_SAMESITE=lax
+
+MAIL_MAILER=log
+MAIL_HOST=null
+MAIL_PORT=2525
+MAIL_FROM=changeme@example.com
+MAIL_USERNAME=null
+MAIL_PASSWORD=null
+MAIL_ENCRYPTION=null
+MAIL_SENDMAIL_COMMAND=
+MAILGUN_DOMAIN=
+MAILGUN_SECRET=
+MAILGUN_ENDPOINT=api.mailgun.net
+MANDRILL_SECRET=
+SPARKPOST_SECRET=
+SEND_ERROR_MESSAGE=true
+SEND_REPORT_JOURNALS=true
+
+ENABLE_EXTERNAL_MAP=false
+ENABLE_EXTERNAL_RATES=false
+MAP_DEFAULT_LAT=51.983333
+MAP_DEFAULT_LONG=5.916667
+MAP_DEFAULT_ZOOM=6
+
+VALID_URL_PROTOCOLS=
+
+AUTHENTICATION_GUARD=web
+AUTHENTICATION_GUARD_HEADER=REMOTE_USER
+AUTHENTICATION_GUARD_EMAIL=
+
+PASSPORT_PRIVATE_KEY=
+PASSPORT_PUBLIC_KEY=
+
+CUSTOM_LOGOUT_URL=
+
+DISABLE_FRAME_HEADER=false
+DISABLE_CSP_HEADER=false
+TRACKER_SITE_ID=
+TRACKER_URL=
+
+ALLOW_WEBHOOKS=false
+
+STATIC_CRON_TOKEN={{ firefly_static_cron_token }}
+
+DKR_BUILD_LOCALE=false
+DKR_CHECK_SQLITE=true
+DKR_RUN_MIGRATION=true
+DKR_RUN_UPGRADE=true
+DKR_RUN_VERIFY=true
+DKR_RUN_REPORT=true
+DKR_RUN_PASSPORT_INSTALL=true
+
+APP_NAME=FireflyIII
+BROADCAST_DRIVER=log
+QUEUE_DRIVER=sync
+CACHE_PREFIX=firefly
+PUSHER_KEY=
+IPINFO_TOKEN=
+PUSHER_SECRET=
+PUSHER_ID=
+DEMO_USERNAME=
+DEMO_PASSWORD=
+FIREFLY_III_LAYOUT=v1
+
+#
+# If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable.
+# It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking.
+# This configuration value WILL NOT HELP.
+#
+# Notable exception to this rule is Synology, which, according to some users, will use APP_URL to rewrite stuff.
+#
+# This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else.
+# So when configuring anything WEB related this variable doesn't do anything. Nothing
+#
+# If you're stuck I understand you get desperate but look SOMEWHERE ELSE.
+#
+APP_URL=http://localhost
diff --git a/roles/firefly/templates/db.env.j2 b/roles/firefly/templates/db.env.j2
new file mode 100644
index 0000000..6805c19
--- /dev/null
+++ b/roles/firefly/templates/db.env.j2
@@ -0,0 +1,5 @@
+POSTGRES_USER={{ firefly_db_username }}
+POSTGRES_PASSWORD={{ firefly_db_password }}
+POSTGRES_DB={{ firefly_db_database }}
+POSTGRES_INITDB_ARGS="--data-checksums"
+POSTGRES_HOST_AUTH_METHOD=scram-sha-256
diff --git a/roles/firefly/templates/docker-compose.yml.j2 b/roles/firefly/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..81ae8f0
--- /dev/null
+++ b/roles/firefly/templates/docker-compose.yml.j2
@@ -0,0 +1,40 @@
+---
+{{ ansible_managed | comment }}
+services:
+ app:
+ image: fireflyiii/core:{{ firefly_version }}
+ hostname: app
+ container_name: firefly_iii_core
+ restart: always
+ volumes:
+ - {{ firefly_home }}/app/upload:/var/www/html/storage/upload
+ env_file: /etc/firefly/app.env
+ networks:
+ - firefly_iii
+ ports:
+ - {{ firefly_port }}:8080
+ depends_on:
+ - db
+ db:
+ image: postgres:17
+ hostname: db
+ container_name: firefly_iii_db
+ restart: always
+ env_file: /etc/firefly/db.env
+ networks:
+ - firefly_iii
+ volumes:
+ - {{ firefly_home }}/db/data:/var/lib/postgresql/data
+ - {{ firefly_home }}/db/backup:/var/lib/postgresql/backup
+
+ cron:
+ image: alpine
+ restart: always
+ container_name: firefly_iii_cron
+ command: sh -c "echo \"0 3 * * * wget -qO- http://app:8080/api/v1/cron/{{ firefly_static_cron_token }}\" | crontab - && crond -f -L /dev/stdout"
+ networks:
+ - firefly_iii
+
+networks:
+ firefly_iii:
+ driver: bridge
diff --git a/roles/forgejo/README.md b/roles/forgejo/README.md
new file mode 100644
index 0000000..8b24841
--- /dev/null
+++ b/roles/forgejo/README.md
@@ -0,0 +1,321 @@
+# Ansible Role Forgejo
+
+Ansible role to manage a [Forgejo](https://forgejo.org/) instance.
+
+## Configuration
+
+See [Variable
+precedence](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#ansible-variable-precedence)
+to find where you should put your own variables.
+
+Then define at least `forgejo_db_password` with a strong and secure password,
+encrypted using
+[ansible-vault](https://docs.ansible.com/ansible/latest/cli/ansible-vault.html).
+
+
+## Table of Content
+
+
+* [Role variables](#variables)
+ * [`forgejo_server`](#variable-forgejo_server)
+ * [`forgejo_version`](#variable-forgejo_version)
+ * [`forgejo_home_dir`](#variable-forgejo_home_dir)
+ * [`forgejo_config_dir`](#variable-forgejo_config_dir)
+ * [`forgejo_web_port`](#variable-forgejo_web_port)
+ * [`forgejo_ssh_port`](#variable-forgejo_ssh_port)
+ * [`forgejo_db_username`](#variable-forgejo_db_username)
+ * [`forgejo_db_password`](#variable-forgejo_db_password)
+ * [`forgejo_db_database`](#variable-forgejo_db_database)
+ * [`forgejo_mailer`](#variable-forgejo_mailer)
+ * [`forgejo_service`](#variable-forgejo_service)
+ * [`forgejo_manage_iptables`](#variable-forgejo_manage_iptables)
+ * [`forgejo_allowed_sources`](#variable-forgejo_allowed_sources)
+ * [`forgejo_runners_version`](#variable-forgejo_runners_version)
+ * [`forgejo_runners_config_dir`](#variable-forgejo_runners_config_dir)
+ * [`forgejo_runners_instance`](#variable-forgejo_runners_instance)
+ * [`forgejo_runners`](#variable-forgejo_runners)
+
+
+
+## Role variables
+
+The following variables can be configured for this role:
+
+| Variable | Type | Required | Default | Description (abstract) |
+|----------|------|----------|---------|------------------------|
+| `forgejo_server` | `bool` | No | `true` | Enable the server mode |
+| `forgejo_version` | `int` | No | `14` | Version of the Forgejo binaries |
+| `forgejo_home_dir` | `path` | No | `"/var/lib/forgejo"` | Path to the home directory |
+| `forgejo_config_dir` | `path` | No | `"/etc/forgejo"` | Path to the configuration directory |
+| `forgejo_web_port` | `int` | No | `3000` | Port to listen for the web UI |
+| `forgejo_ssh_port` | `int` | No | `222` | Port to listen for SSH |
+| `forgejo_db_username` | `str` | No | `"forgejo"` | Name of the user in the database |
+| `forgejo_db_password` | `str` | Yes | N/A | Password of the user in the database |
+| `forgejo_db_database` | `str` | No | `"forgejo"` | Name of the database |
+| `forgejo_mailer` | `dict` | No | N/A | Configure the mailer to send e-mail notifications
Define a `enabled` key with a boolean to enable the mailer
Define a `from` key with the source e-mail address
See [Email setup](https://forgejo.org/docs/latest/admin/setup/email/) |
+| `forgejo_service` | `dict` | No | N/A | Configure service settings
See [Service](https://forgejo.org/docs/latest/admin/config-cheat-sheet/#service-service) |
+| `forgejo_manage_iptables` | `bool` | No | `false` | Configure iptables rules |
+| `forgejo_allowed_sources` | `list` | No | N/A | List of IP ranges to allow when `forgejo_manage_iptables` is enabled |
+| `forgejo_runners_version` | `str` | No | `"9.1.1"` | Version of the runners |
+| `forgejo_runners_config_dir` | `path` | No | `"/etc/forgejo-runners"` | Path to the configuration directory of the runners |
+| `forgejo_runners_instance` | `str` | No | N/A | URL of the Forgejo instance to register the runners |
+| `forgejo_runners` | `dict` | No | N/A | List of runners to configure
The key is the name of the repository on the instance
The value is a dict with a `token` key and optionally a dict of `labels` |
+
+### `forgejo_server`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Enable the server mode
+
+- **Type**: `bool`
+- **Required**: No
+- **Default**: `true`
+
+
+
+### `forgejo_version`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Version of the Forgejo binaries
+
+- **Type**: `int`
+- **Required**: No
+- **Default**: `14`
+
+
+
+### `forgejo_home_dir`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Path to the home directory
+
+- **Type**: `path`
+- **Required**: No
+- **Default**: `"/var/lib/forgejo"`
+
+
+
+### `forgejo_config_dir`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Path to the configuration directory
+
+- **Type**: `path`
+- **Required**: No
+- **Default**: `"/etc/forgejo"`
+
+
+
+### `forgejo_web_port`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Port to listen for the web UI
+
+- **Type**: `int`
+- **Required**: No
+- **Default**: `3000`
+
+
+
+### `forgejo_ssh_port`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Port to listen for SSH
+
+- **Type**: `int`
+- **Required**: No
+- **Default**: `222`
+
+
+
+### `forgejo_db_username`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Name of the user in the database
+
+- **Type**: `str`
+- **Required**: No
+- **Default**: `"forgejo"`
+
+
+
+### `forgejo_db_password`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Password of the user in the database
+
+- **Type**: `str`
+- **Required**: Yes
+
+
+
+### `forgejo_db_database`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Name of the database
+
+- **Type**: `str`
+- **Required**: No
+- **Default**: `"forgejo"`
+
+
+
+### `forgejo_mailer`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Configure the mailer to send e-mail notifications
+
+Define a `enabled` key with a boolean to enable the mailer
+
+Define a `from` key with the source e-mail address
+
+See [Email setup](https://forgejo.org/docs/latest/admin/setup/email/)
+
+- **Type**: `dict`
+- **Required**: No
+
+
+
+### `forgejo_service`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Configure service settings
+
+See [Service](https://forgejo.org/docs/latest/admin/config-cheat-sheet/#service-service)
+
+- **Type**: `dict`
+- **Required**: No
+
+
+
+### `forgejo_manage_iptables`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Configure iptables rules
+
+- **Type**: `bool`
+- **Required**: No
+- **Default**: `false`
+
+
+
+### `forgejo_allowed_sources`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+List of IP ranges to allow when `forgejo_manage_iptables` is enabled
+
+- **Type**: `list`
+- **Required**: No
+
+
+
+### `forgejo_runners_version`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Version of the runners
+
+- **Type**: `str`
+- **Required**: No
+- **Default**: `"9.1.1"`
+
+
+
+### `forgejo_runners_config_dir`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+Path to the configuration directory of the runners
+
+- **Type**: `path`
+- **Required**: No
+- **Default**: `"/etc/forgejo-runners"`
+
+
+
+### `forgejo_runners_instance`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+URL of the Forgejo instance to register the runners
+
+- **Type**: `str`
+- **Required**: No
+
+
+
+### `forgejo_runners`
+
+[*⇑ Back to ToC ⇑*](#toc)
+
+List of runners to configure
+
+The key is the name of the repository on the instance
+
+The value is a dict with a `token` key and optionally a dict of `labels`
+
+- **Type**: `dict`
+- **Required**: No
+
+
+
+
+
+
+## Usage
+
+Example of a basic forgejo.yml playbook:
+
+```yaml
+- hosts: forgejo
+ roles:
+ - jriou.forgejo
+```
+
+Then run the playbook:
+
+```
+ansible-playbook forgejo.yml
+```
+
+## Runners
+
+Example of runners configuration:
+
+```yaml
+- hosts: forgejo
+ roles:
+ - jriou.forgejo
+ vars:
+ forgejo_server: false
+ forgejo_runners_instance: https://codeberg.org # FIXME
+ forgejo_runners:
+ coller:
+ token: **redacted**
+ labels:
+ debian12: docker://data.forgejo.org/oci/debian:bookworm
+ debian13: docker://data.forgejo.org/oci/debian:trixie
+ ansible:
+ token: **redacted**
+ labels:
+ docker: docker://data.forgejo.org/oci/node:latest
+```
+
+
+## Donate
+
+As we all love FOSS projects, you should consider [donating to
+Codeberg](https://donate.codeberg.org/), the non-profit organization behind
+Forgejo.
diff --git a/roles/forgejo/defaults/main.yml b/roles/forgejo/defaults/main.yml
new file mode 100644
index 0000000..1ba666e
--- /dev/null
+++ b/roles/forgejo/defaults/main.yml
@@ -0,0 +1,120 @@
+---
+
+# Enable the server mode
+#
+# - Type: bool
+# - Required: No
+# - Default: true
+forgejo_server: true
+
+# Version of the Forgejo binaries
+#
+# - Type: int
+# - Required: No
+# - Default: 14
+forgejo_version: 14
+
+# Path to the home directory
+#
+# - Type: path
+# - Required: No
+# - Default: /var/lib/forgejo
+forgejo_home_dir: /var/lib/forgejo
+
+# Path to the configuration directory
+#
+# - Type: path
+# - Required: No
+# - Default: /etc/forgejo
+forgejo_config_dir: /etc/forgejo
+
+# Port to listen for the web UI
+#
+# - Type: int
+# - Required: No
+# - Default: 3000
+forgejo_web_port: 3000
+
+# Port to listen for SSH
+#
+# - Type: int
+# - Required: No
+# - Default: 222
+forgejo_ssh_port: 222
+
+# Name of the user in the database
+#
+# - Type: str
+# - Required: No
+# - Default: forgejo
+forgejo_db_username: forgejo
+
+# Password of the user in the database
+#
+# - Type: str
+# - Required: Yes
+forgejo_db_password: CHANGEME
+
+# Name of the database
+#
+# - Type: str
+# - Required: No
+# - Default: forgejo
+forgejo_db_database: forgejo
+
+# Configure the mailer to send e-mail notifications
+#
+# Define a `enabled` key with a boolean to enable the mailer
+#
+# Define a `from` key with the source e-mail address
+#
+# See
+#
+# - Type: dict
+# - Required: No
+forgejo_mailer: {}
+
+# Configure service settings
+#
+# See
+#
+# - Type: dict
+# - Required: No
+forgejo_service: {}
+
+# Configure iptables rules
+#
+# - Type: bool
+# - Required: No
+# - Default: false
+forgejo_manage_iptables: false
+
+# List of IP ranges to allow when `forgejo_manage_iptables` is enabled
+#
+# - Type: list
+# - Required: No
+forgejo_allowed_sources: []
+
+# Version of the runners
+#
+# - Type: str
+# - Required: No
+# - Default: 9.1.1
+forgejo_runners_version: 9.1.1
+
+# Path to the configuration directory of the runners
+#
+# - Type: path
+# - Required: No
+# - Default: /etc/forgejo-runners
+forgejo_runners_config_dir: /etc/forgejo-runners
+
+# List of runners to configure
+#
+# The key is the name of the repository on the instance
+#
+# The value is a dict with a `token` key and optionally a dict of `labels`
+#
+# - Type: dict
+# - Required: No
+forgejo_runners: {}
diff --git a/roles/forgejo/handlers/main.yml b/roles/forgejo/handlers/main.yml
new file mode 100644
index 0000000..e371990
--- /dev/null
+++ b/roles/forgejo/handlers/main.yml
@@ -0,0 +1,17 @@
+---
+- name: save iptables
+ ansible.builtin.shell:
+ cmd: netfilter-persistent save
+
+- name: start runners
+ community.docker.docker_compose_v2:
+ project_src: "{{ forgejo_runners_config_dir }}"
+ files:
+ - docker-compose.yml
+
+- name: restart runners
+ community.docker.docker_compose_v2:
+ project_src: "{{ forgejo_runners_config_dir }}"
+ files:
+ - docker-compose.yml
+ state: restarted
diff --git a/roles/forgejo/meta/argument_specs.yml b/roles/forgejo/meta/argument_specs.yml
new file mode 100644
index 0000000..d3fe081
--- /dev/null
+++ b/roles/forgejo/meta/argument_specs.yml
@@ -0,0 +1,106 @@
+---
+argument_specs:
+ main:
+ short_description: Install and configure a Forgejo instance
+ description:
+ - Install and configure a [Forgejo](https://forgejo.org/) instance.
+ author:
+ - jriou
+ options:
+ forgejo_server:
+ description:
+ - Enable the server mode
+ type: bool
+ default: true
+
+ forgejo_version:
+ description:
+ - Version of the Forgejo binaries
+ type: int
+ default: 14
+
+ forgejo_home_dir:
+ description:
+ - Path to the home directory
+ type: path
+ default: /var/lib/forgejo
+
+ forgejo_config_dir:
+ description:
+ - Path to the configuration directory
+ type: path
+ default: /etc/forgejo
+
+ forgejo_web_port:
+ description:
+ - Port to listen for the web UI
+ type: int
+ default: 3000
+
+ forgejo_ssh_port:
+ description:
+ - Port to listen for SSH
+ type: int
+ default: 222
+
+ forgejo_db_username:
+ description:
+ - Name of the user in the database
+ default: forgejo
+
+ forgejo_db_password:
+ description:
+ - Password of the user in the database
+ required: true
+
+ forgejo_db_database:
+ description:
+ - Name of the database
+ default: forgejo
+
+ forgejo_mailer:
+ description:
+ - Configure the mailer to send e-mail notifications
+ - Define a `enabled` key with a boolean to enable the mailer
+ - Define a `from` key with the source e-mail address
+ - See [Email setup](https://forgejo.org/docs/latest/admin/setup/email/)
+ type: dict
+
+ forgejo_service:
+ description:
+ - Configure service settings
+ - See [Service](https://forgejo.org/docs/latest/admin/config-cheat-sheet/#service-service)
+ type: dict
+
+ forgejo_manage_iptables:
+ description:
+ - Configure iptables rules
+ type: bool
+ default: false
+
+ forgejo_allowed_sources:
+ description:
+ - List of IP ranges to allow when `forgejo_manage_iptables` is enabled
+ type: list
+
+ forgejo_runners_version:
+ description:
+ - Version of the runners
+ default: 9.1.1
+
+ forgejo_runners_config_dir:
+ description:
+ - Path to the configuration directory of the runners
+ type: path
+ default: /etc/forgejo-runners
+
+ forgejo_runners_instance:
+ description:
+ - URL of the Forgejo instance to register the runners
+
+ forgejo_runners:
+ description:
+ - List of runners to configure
+ - The key is the name of the repository on the instance
+ - The value is a dict with a `token` key and optionally a dict of `labels`
+ type: dict
diff --git a/roles/forgejo/meta/main.yml b/roles/forgejo/meta/main.yml
new file mode 100644
index 0000000..94ae890
--- /dev/null
+++ b/roles/forgejo/meta/main.yml
@@ -0,0 +1,18 @@
+---
+dependencies:
+- role: geerlingguy.docker
+
+galaxy_info:
+ role_name: jriou.forgejo
+ author: jriou
+ description: Ansible role to manage a Forgejo instance
+ license_file: LICENSE
+ min_ansible_version: 2.18
+ platforms:
+ - name: Debian
+ versions:
+ - bullseye
+ - bookworm
+ - trixie
+ galaxy_tags:
+ - forgejo
diff --git a/roles/forgejo/tasks/deploy-runners.yml b/roles/forgejo/tasks/deploy-runners.yml
new file mode 100644
index 0000000..6dc8e02
--- /dev/null
+++ b/roles/forgejo/tasks/deploy-runners.yml
@@ -0,0 +1,20 @@
+---
+- name: register runners
+ ansible.builtin.include_tasks: register-runner.yml
+ loop: "{{ forgejo_runners | dict2items }}"
+ loop_control:
+ label: "{{ item.key }}"
+
+- name: create runners configuration
+ ansible.builtin.template:
+ src: "runners/docker-compose.yml.j2"
+ dest: "{{ forgejo_runners_config_dir }}/docker-compose.yml"
+ owner: root
+ group: root
+ mode: "0644"
+
+- name: start runners service
+ community.docker.docker_compose_v2:
+ project_src: "{{ forgejo_runners_config_dir }}"
+ files:
+ - docker-compose.yml
diff --git a/roles/forgejo/tasks/deploy-server.yml b/roles/forgejo/tasks/deploy-server.yml
new file mode 100644
index 0000000..a2c7dbf
--- /dev/null
+++ b/roles/forgejo/tasks/deploy-server.yml
@@ -0,0 +1,61 @@
+---
+- name: check database password
+ ansible.builtin.assert:
+ that: forgejo_db_password is defined
+
+- name: create directories
+ ansible.builtin.file:
+ state: directory
+ path: "{{ item }}"
+ owner: forgejo
+ group: forgejo
+ mode: "0755"
+ loop: &forgejo_directories
+ - "{{ forgejo_config_dir }}"
+ - "{{ forgejo_home_dir }}"
+ - "{{ forgejo_home_dir }}/server"
+ - "{{ forgejo_home_dir }}/db"
+
+- name: ensure permissions on those directories
+ ansible.builtin.command:
+ cmd: "chown -R forgejo:forgejo {{ item }}"
+ loop: *forgejo_directories
+
+- name: create docker-compose configuration
+ ansible.builtin.template:
+ src: "{{ item.name }}.j2"
+ dest: "{{ forgejo_config_dir }}/{{ item.name }}"
+ owner: root
+ group: root
+ mode: "{{ item.mode }}"
+ loop:
+ - name: docker-compose.yml
+ mode: "0644"
+ - name: server.env
+ mode: "0600"
+ - name: db.env
+ mode: "0600"
+
+- name: start service
+ community.docker.docker_compose_v2:
+ project_src: "{{ forgejo_config_dir }}"
+ files:
+ - docker-compose.yml
+
+- name: allow with iptables
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ source: "{{ item }}"
+ destination_ports:
+ - "{{ forgejo_web_port }}"
+ - "{{ forgejo_ssh_port }}"
+ jump: ACCEPT
+ comment: forgejo
+ loop: "{{ forgejo_allowed_sources }}"
+ notify: save iptables
+ when: forgejo_manage_iptables
+
+- name: deploy runners
+ ansible.builtin.include_tasks: deploy-runners.yml
+ when: forgejo_runners
diff --git a/roles/forgejo/tasks/main.yml b/roles/forgejo/tasks/main.yml
new file mode 100644
index 0000000..c845b1c
--- /dev/null
+++ b/roles/forgejo/tasks/main.yml
@@ -0,0 +1,21 @@
+---
+- name: add forgejo user
+ ansible.builtin.user:
+ name: forgejo
+ system: yes
+ password: '!'
+ home: "{{ forgejo_home_dir }}"
+ create_home: no
+
+- name: read forgejo attributes
+ ansible.builtin.getent:
+ database: passwd
+ key: forgejo
+
+- name: deploy server
+ ansible.builtin.include_tasks: deploy-server.yml
+ when: forgejo_server
+
+- name: deploy runners
+ ansible.builtin.include_tasks: deploy-runners.yml
+ when: forgejo_runners is defined
diff --git a/roles/forgejo/tasks/register-runner.yml b/roles/forgejo/tasks/register-runner.yml
new file mode 100644
index 0000000..9b63297
--- /dev/null
+++ b/roles/forgejo/tasks/register-runner.yml
@@ -0,0 +1,39 @@
+---
+- name: check variables
+ ansible.builtin.assert:
+ that:
+ - forgejo_runners_instance is defined
+ - forgejo_runners_version is defined
+ - forgejo_runners_config_dir is defined
+ - "'key' in item"
+ - "'value' in item"
+
+- name: create runner subdirectory
+ ansible.builtin.file:
+ path: "{{ forgejo_runners_config_dir }}/{{ item.key }}"
+ state: directory
+ mode: "0755"
+ owner: forgejo
+ group: forgejo
+
+- name: register runner
+ ansible.builtin.command:
+ cmd: >-
+ docker run
+ -v /var/run/docker.sock:/var/run/docker.sock
+ -v {{ forgejo_runners_config_dir }}/{{ item.key }}:/data
+ --rm
+ --user {{ ansible_facts.getent_passwd.forgejo[1] }}:{{ ansible_facts.getent_passwd.forgejo[2] }}
+ code.forgejo.org/forgejo/runner:{{ forgejo_runners_version }}
+ forgejo-runner register --no-interactive
+ --token {{ item.value.token }}
+ --name {{ item.key }}
+ --instance {{ forgejo_runners_instance }}
+ creates: "{{ forgejo_runners_config_dir }}/{{ item.key }}/.runner"
+ notify: start runners
+
+- name: create runner configuration
+ ansible.builtin.template:
+ src: runners/config.yml.j2
+ dest: "{{ forgejo_runners_config_dir }}/{{ item.key }}/config.yml"
+ notify: restart runners
diff --git a/roles/forgejo/templates/db.env.j2 b/roles/forgejo/templates/db.env.j2
new file mode 100644
index 0000000..ba5ecbd
--- /dev/null
+++ b/roles/forgejo/templates/db.env.j2
@@ -0,0 +1,6 @@
+{{ ansible_managed | comment }}
+POSTGRES_USER="{{ forgejo_db_username }}"
+POSTGRES_PASSWORD="{{ forgejo_db_password }}"
+POSTGRES_DB="{{ forgejo_db_database }}"
+POSTGRES_INITDB_ARGS="--data-checksums"
+POSTGRES_HOST_AUTH_METHOD=scram-sha-256
diff --git a/roles/forgejo/templates/docker-compose.yml.j2 b/roles/forgejo/templates/docker-compose.yml.j2
new file mode 100644
index 0000000..d54e1d7
--- /dev/null
+++ b/roles/forgejo/templates/docker-compose.yml.j2
@@ -0,0 +1,35 @@
+---
+{{ ansible_managed | comment }}
+services:
+ server:
+ image: codeberg.org/forgejo/forgejo:{{ forgejo_version }}
+ container_name: forgejo-server
+ env_file: {{ forgejo_config_dir }}/server.env
+ restart: always
+ networks:
+ - forgejo
+ volumes:
+ - "{{ forgejo_home_dir }}/server:/data"
+ - /etc/timezone:/etc/timezone:ro
+ - /etc/localtime:/etc/localtime:ro
+ ports:
+ - "{{ forgejo_web_port }}:3000"
+ - "{{ forgejo_ssh_port }}:22"
+ depends_on:
+ - db
+
+ db:
+ image: postgres:17
+ hostname: db
+ container_name: forgejo-db
+ restart: always
+ env_file: {{ forgejo_config_dir }}/db.env
+ user: "{{ ansible_facts.getent_passwd.forgejo[1] }}:{{ ansible_facts.getent_passwd.forgejo[2] }}"
+ networks:
+ - forgejo
+ volumes:
+ - "{{ forgejo_home_dir }}/db:/var/lib/postgresql/data"
+
+networks:
+ forgejo:
+ external: false
diff --git a/roles/forgejo/templates/runners/config.yml.j2 b/roles/forgejo/templates/runners/config.yml.j2
new file mode 100644
index 0000000..39eb084
--- /dev/null
+++ b/roles/forgejo/templates/runners/config.yml.j2
@@ -0,0 +1,44 @@
+{{ ansible_managed | comment }}
+log:
+ level: info
+ job_level: info
+
+runner:
+ file: .runner
+ capacity: 1
+ timeout: 3h
+ shutdown_timeout: 3h
+ insecure: false
+ fetch_timeout: 5s
+ fetch_interval: 2s
+ report_interval: 1s
+{% if item.value.labels | default({}) %}
+ labels:
+{% for label_name, label_value in item.value.labels.items() %}
+ - "{{ label_name }}:{{ label_value }}"
+{% endfor %}
+{% endif %}
+
+cache:
+ enabled: true
+ port: 0
+ dir: ""
+ external_server: ""
+ secret: ""
+ host: ""
+ proxy_port: 0
+ actions_cache_url_override: ""
+
+container:
+ network: ""
+ enable_ipv6: false
+ privileged: false
+ options:
+ workdir_parent:
+ valid_volumes: []
+ docker_host: "-"
+ force_pull: false
+ force_rebuild: false
+
+host:
+ workdir_parent:
diff --git a/roles/forgejo/templates/runners/docker-compose.yml.j2 b/roles/forgejo/templates/runners/docker-compose.yml.j2
new file mode 100644
index 0000000..a6eb7f5
--- /dev/null
+++ b/roles/forgejo/templates/runners/docker-compose.yml.j2
@@ -0,0 +1,27 @@
+---
+{{ ansible_managed | comment }}
+services:
+ docker:
+ image: docker:dind
+ privileged: true
+ restart: always
+ volumes:
+ - certs:/certs
+
+{% for runner in forgejo_runners %}
+ runner-{{ runner }}:
+ image: code.forgejo.org/forgejo/runner:{{ forgejo_runners_version }}
+ user: {{ ansible_facts.getent_passwd.forgejo[1] }}:{{ ansible_facts.getent_passwd.forgejo[2] }}
+ environment:
+ DOCKER_HOST: tcp://docker:2376
+ DOCKER_TLS_VERIFY: 1
+ DOCKER_CERT_PATH: /certs/client
+ restart: always
+ volumes:
+ - {{ forgejo_runners_config_dir }}/{{ runner }}:/data
+ - certs:/certs
+ command: 'forgejo-runner --config config.yml daemon'
+{% endfor %}
+
+volumes:
+ certs:
diff --git a/roles/forgejo/templates/server.env.j2 b/roles/forgejo/templates/server.env.j2
new file mode 100644
index 0000000..0bf2d4c
--- /dev/null
+++ b/roles/forgejo/templates/server.env.j2
@@ -0,0 +1,19 @@
+{{ ansible_managed | comment }}
+USER_UID={{ ansible_facts.getent_passwd.forgejo[1] }}
+USER_GID={{ ansible_facts.getent_passwd.forgejo[2] }}
+FORGEJO__server__SSH_PORT={{ forgejo_ssh_port }}
+FORGEJO__database__DB_TYPE=postgres
+FORGEJO__database__HOST=db:5432
+FORGEJO__database__NAME="{{ forgejo_db_database }}"
+FORGEJO__database__USER="{{ forgejo_db_username }}"
+FORGEJO__database__PASSWD="{{ forgejo_db_password }}"
+{% if forgejo_mailer %}
+{% for k, v in forgejo_mailer.items() %}
+FORGEJO__mailer__{{ k | upper }}="{{ v }}"
+{% endfor %}
+{% endif %}
+{% if forgejo_service %}
+{% for k, v in forgejo_service.items() %}
+FORGEJO__service__{{ k | upper }}="{{ v }}"
+{% endfor %}
+{% endif %}
diff --git a/roles/galene/defaults/main.yml b/roles/galene/defaults/main.yml
new file mode 100644
index 0000000..56f3852
--- /dev/null
+++ b/roles/galene/defaults/main.yml
@@ -0,0 +1,24 @@
+---
+galene_version: galene-1.0
+galene_http_port: 443
+galene_turn: ":1194"
+galene_user: galene
+galene_group: galene
+galene_base_directory: /var/lib/galene
+galene_data_directory: "{{ galene_base_directory }}/data"
+galene_groups_directory: "{{ galene_base_directory }}/groups"
+galene_recording_directory: "{{ galene_base_directory }}/recordings"
+galene_static_directory: "{{ galene_base_directory }}/static"
+# galene_domain:
+
+# galene_config:
+# canonicalHost: galene.example.org
+galene_config: {}
+
+# galene_groups:
+# example:
+# users:
+# bob:
+# password: ***
+# permissions: op
+galene_groups: {}
diff --git a/roles/galene/handlers/main.yml b/roles/galene/handlers/main.yml
new file mode 100644
index 0000000..24198bf
--- /dev/null
+++ b/roles/galene/handlers/main.yml
@@ -0,0 +1,9 @@
+---
+- name: Reload systemd
+ ansible.builtin.systemd_service:
+ daemon_reload: true
+
+- name: Restart galene
+ ansible.builtin.service:
+ name: galene
+ state: restarted
diff --git a/roles/galene/meta/main.yml b/roles/galene/meta/main.yml
new file mode 100644
index 0000000..affff51
--- /dev/null
+++ b/roles/galene/meta/main.yml
@@ -0,0 +1,3 @@
+---
+dependencies:
+ - role: golang
diff --git a/roles/galene/tasks/main.yml b/roles/galene/tasks/main.yml
new file mode 100644
index 0000000..e8570b4
--- /dev/null
+++ b/roles/galene/tasks/main.yml
@@ -0,0 +1,116 @@
+---
+# TODO: install in block
+- name: Install requirements
+ ansible.builtin.package:
+ name: git
+
+- name: Clone source code
+ ansible.builtin.git:
+ repo: https://github.com/jech/galene
+ dest: /opt/galene
+ version: "{{ galene_version }}"
+
+- name: Compile
+ ansible.builtin.command:
+ chdir: /opt/galene
+ cmd: go build -ldflags='-s -w'
+ creates: /opt/galene/galene
+ environment:
+ CGO_ENABLED: "0"
+ PATH: /usr/local/go/bin
+
+- name: Install
+ ansible.builtin.copy:
+ remote_src: true
+ src: /opt/galene/galene
+ dest: /usr/local/bin/galene
+ owner: root
+ group: root
+ mode: "0755"
+# TODO End of install in block
+
+- name: Create user
+ ansible.builtin.user:
+ name: "{{ galene_user }}"
+ system: true
+ password: '!'
+ home: "{{ galene_base_directory }}"
+ create_home: false
+
+- name: Create directories
+ ansible.builtin.file:
+ path: "{{ item }}"
+ state: directory
+ owner: "{{ galene_user }}"
+ group: "{{ galene_group }}"
+ mode: "0755"
+ loop:
+ - "{{ galene_base_directory }}"
+ - "{{ galene_data_directory }}"
+ - "{{ galene_groups_directory }}"
+ - "{{ galene_recording_directory }}"
+ - "{{ galene_static_directory }}"
+
+- name: Copy static directory
+ ansible.builtin.copy:
+ src: /opt/galene/static/
+ dest: "{{ galene_static_directory }}/"
+ remote_src: true
+ mode: "0755"
+ owner: "{{ galene_user }}"
+ group: "{{ galene_group }}"
+ when: galene_static_directory != "/opt/galene/static"
+
+- name: Configure groups
+ ansible.builtin.copy:
+ content: "{{ item.value | to_json }}"
+ dest: "{{ galene_groups_directory }}/{{ item.key }}.json"
+ owner: "{{ galene_user }}"
+ group: "{{ galene_group }}"
+ mode: "0600"
+ loop: "{{ galene_groups | dict2items }}"
+ loop_control:
+ label: "{{ item.key }}"
+ notify: Restart galene
+
+- name: Create global configuration
+ ansible.builtin.copy:
+ content: "{{ galene_config | to_json }}"
+ dest: "{{ galene_data_directory }}/config.json"
+ owner: "{{ galene_user }}"
+ group: "{{ galene_group }}"
+ mode: "0600"
+ notify: Restart galene
+
+- name: Configure TLS certificates
+ when: galene_domain is defined
+ ansible.builtin.copy:
+ remote_src: true
+ src: "{{ item.src }}"
+ dest: "{{ item.dest }}"
+ owner: "{{ galene_user }}"
+ group: "{{ galene_group }}"
+ loop:
+ - src: "/etc/letsencrypt/live/{{ galene_domain }}/fullchain.pem"
+ dest: "{{ galene_data_directory }}/cert.pem"
+ mode: "0644"
+ - src: "/etc/letsencrypt/live/{{ galene_domain }}/privkey.pem"
+ dest: "{{ galene_data_directory }}/key.pem"
+ mode: "0600"
+
+- name: Create service
+ ansible.builtin.template:
+ src: galene.service.j2
+ dest: /etc/systemd/system/galene.service
+ mode: "0644"
+ owner: root
+ group: root
+ notify:
+ - Reload systemd
+ - Restart galene
+
+- name: Start service
+ ansible.builtin.service:
+ name: galene
+ state: started
+ enabled: true
diff --git a/roles/galene/templates/galene.service.j2 b/roles/galene/templates/galene.service.j2
new file mode 100644
index 0000000..5a90399
--- /dev/null
+++ b/roles/galene/templates/galene.service.j2
@@ -0,0 +1,19 @@
+{{ ansible_managed | comment }}
+
+[Unit]
+Description=Galene
+After=network.target
+
+[Service]
+Type=simple
+WorkingDirectory={{ galene_base_directory }}
+User={{ galene_user }}
+Group={{ galene_group }}
+{% if galene_http_port < 1024 %}
+AmbientCapabilities=CAP_NET_BIND_SERVICE
+{% endif %}
+ExecStart=/usr/local/bin/galene -http :{{ galene_http_port }} -data {{ galene_data_directory }} -groups {{ galene_groups_directory }} -recordings {{ galene_recording_directory }} -static {{ galene_static_directory }} -turn "{{ galene_turn }}"
+LimitNOFILE=65536
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/golang/defaults/main.yml b/roles/golang/defaults/main.yml
new file mode 100644
index 0000000..f969c1d
--- /dev/null
+++ b/roles/golang/defaults/main.yml
@@ -0,0 +1,2 @@
+---
+golang_version: 1.25.4
diff --git a/roles/golang/tasks/main.yml b/roles/golang/tasks/main.yml
new file mode 100644
index 0000000..6af3007
--- /dev/null
+++ b/roles/golang/tasks/main.yml
@@ -0,0 +1,7 @@
+---
+- name: Install
+ ansible.builtin.unarchive:
+ src: "https://go.dev/dl/go{{ golang_version }}.linux-amd64.tar.gz"
+ dest: /usr/local
+ remote_src: true
+ creates: /usr/local/go
diff --git a/roles/navidrome/.gitignore b/roles/navidrome/.gitignore
new file mode 100644
index 0000000..5ceb386
--- /dev/null
+++ b/roles/navidrome/.gitignore
@@ -0,0 +1 @@
+venv
diff --git a/roles/navidrome/README.md b/roles/navidrome/README.md
new file mode 100644
index 0000000..d00778c
--- /dev/null
+++ b/roles/navidrome/README.md
@@ -0,0 +1,35 @@
+# Ansible Role Navidrome
+
+Ansible role to manage a [Navidrome](https://github.com/navidrome/navidrome) instance.
+
+## Configuration
+
+See [Variable
+precedence](https://docs.ansible.com/ansible/latest/playbook_guide/playbooks_variables.html#ansible-variable-precedence)
+to find where you should put your own variables.
+
+See list of [default
+variables](https://git.riou.xyz/jriou/ansible-role-navidrome/src/branch/main/defaults/main.yml).
+
+## Usage
+
+Example of a basic navidrome.yml playbook:
+
+```yaml
+hosts:
+ - navidrome
+
+roles:
+ - navidrome
+```
+
+Then run the playbook:
+
+```
+ansible-playbook navidrome.yml
+```
+
+## Donate
+
+As we all love FOSS, you should consider sponsoring the
+[Navidrome](https://github.com/navidrome/navidrome) project.
diff --git a/roles/navidrome/defaults/main.yml b/roles/navidrome/defaults/main.yml
new file mode 100644
index 0000000..478998b
--- /dev/null
+++ b/roles/navidrome/defaults/main.yml
@@ -0,0 +1,14 @@
+---
+navidrome_version: 0.54.5
+navidrome_arch: amd64
+navidrome_user: navidrome
+navidrome_group: navidrome
+navidrome_music_folder: /opt/navidrome/music
+navidrome_data_folder: /var/lib/navidrome
+navidrome_cache_folder: "{{ navidrome_data_folder }}/cache"
+navidrome_manage_iptables: false
+navidrome_allowed_sources: []
+navidrome_address: localhost
+navidrome_port: 4533
+navidrome_enable_insights_collector: false
+# navidrome_base_url:
diff --git a/roles/navidrome/handlers/main.yml b/roles/navidrome/handlers/main.yml
new file mode 100644
index 0000000..152799e
--- /dev/null
+++ b/roles/navidrome/handlers/main.yml
@@ -0,0 +1,14 @@
+---
+- name: Reload systemd
+ ansible.builtin.systemd_service:
+ daemon_reload: true
+
+- name: Restart navidrome
+ ansible.builtin.service:
+ name: navidrome
+ state: restarted
+
+- name: Save iptables
+ ansible.builtin.command:
+ cmd: netfilter-persistent save
+ changed_when: true
diff --git a/roles/navidrome/tasks/main.yml b/roles/navidrome/tasks/main.yml
new file mode 100644
index 0000000..d575f0e
--- /dev/null
+++ b/roles/navidrome/tasks/main.yml
@@ -0,0 +1,62 @@
+---
+- name: Install package
+ ansible.builtin.apt:
+ deb: "https://github.com/navidrome/navidrome/releases/download/v{{ navidrome_version }}/navidrome_{{ navidrome_version }}_linux_{{ navidrome_arch }}.deb"
+
+- name: Create directories
+ ansible.builtin.file:
+ state: directory
+ path: "{{ item }}"
+ owner: "{{ navidrome_user }}"
+ group: "{{ navidrome_group }}"
+ mode: "0755"
+ loop:
+ - "{{ navidrome_music_folder }}"
+ - "{{ navidrome_data_folder }}"
+ - "{{ navidrome_cache_folder }}"
+
+- name: Create configuration file
+ ansible.builtin.template:
+ src: navidrome.toml.j2
+ dest: /etc/navidrome/navidrome.toml
+ owner: "{{ navidrome_user }}"
+ group: "{{ navidrome_group }}"
+ mode: "0644"
+ notify:
+ - Restart navidrome
+
+- name: Create systemd override directory
+ ansible.builtin.file:
+ state: directory
+ path: /etc/systemd/system/navidrome.service.d
+ owner: root
+ group: root
+ mode: "0755"
+
+- name: Add systemd override
+ ansible.builtin.template:
+ src: override.conf.j2
+ dest: /etc/systemd/system/navidrome.service.d/override.conf
+ owner: root
+ group: root
+ mode: "0755"
+ notify:
+ - Reload systemd
+ - Restart navidrome
+
+- name: Start service
+ ansible.builtin.service:
+ name: navidrome
+ state: started
+
+- name: Manage iptables
+ ansible.builtin.iptables:
+ chain: INPUT
+ protocol: tcp
+ source: "{{ item }}"
+ destination_port: "{{ navidrome_port }}"
+ jump: ACCEPT
+ comment: navidrome
+ loop: "{{ navidrome_allowed_sources }}"
+ notify: Save iptables
+ when: navidrome_manage_iptables
diff --git a/roles/navidrome/templates/navidrome.toml.j2 b/roles/navidrome/templates/navidrome.toml.j2
new file mode 100644
index 0000000..2b6492b
--- /dev/null
+++ b/roles/navidrome/templates/navidrome.toml.j2
@@ -0,0 +1,10 @@
+{{ ansible_managed | comment }}
+MusicFolder = "{{ navidrome_music_folder }}"
+DataFolder = "{{ navidrome_data_folder }}"
+CacheFolder = "{{ navidrome_cache_folder }}"
+Address = "{{ navidrome_address }}"
+Port = "{{ navidrome_port }}"
+EnableInsightsCollector = "{{ navidrome_enable_insights_collector }}"
+{% if navidrome_base_url is defined %}
+BaseUrl = "{{ navidrome_base_url }}"
+{% endif %}
diff --git a/roles/navidrome/templates/override.conf.j2 b/roles/navidrome/templates/override.conf.j2
new file mode 100644
index 0000000..371ef28
--- /dev/null
+++ b/roles/navidrome/templates/override.conf.j2
@@ -0,0 +1,5 @@
+{{ ansible_managed | comment }}
+[Service]
+User={{ navidrome_user }}
+WorkingDirectory={{ navidrome_data_folder }}
+ReadWritePaths={{ navidrome_data_folder }}