Initial commit
Some checks failed
/ ansible-docsmith (push) Successful in 50s
/ molecule (certbot) (push) Failing after 29s
/ molecule (galene) (push) Failing after 1m5s
/ molecule (golang) (push) Has been cancelled

Signed-off-by: Julien Riou <julien@riou.xyz>
This commit is contained in:
Julien Riou 2026-02-16 18:57:09 +01:00 committed by Julien Riou
commit 90e35576e6
Signed by: jriou
GPG key ID: 9A099EDA51316854
92 changed files with 3529 additions and 0 deletions

View file

@ -0,0 +1,64 @@
---
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: |
for role_dir in $(ls --color=none roles); do
ansible-docsmith generate "roles/${role_dir}"
done
- 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
molecule:
runs-on: node-latest
strategy:
matrix:
role:
- certbot
- galene
- golang
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install requirements.txt
run: |
apt-get update
apt-get install -y python3-pip
pip install -r test-requirements.txt --break-system-packages
- name: Install requirements.yml
run: |
ansible-galaxy collection install -r requirements.yml
- name: Run molecule
run: molecule test --all
chdir: "roles/${{ matrix.role }}"

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
venv

9
LICENSE Normal file
View file

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2026 jriou
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

3
README.md Normal file
View file

@ -0,0 +1,3 @@
# Ansible
My Ansible collection.

29
galaxy.yml Normal file
View file

@ -0,0 +1,29 @@
---
namespace: jriou
name: general
version: 1.0.0
readme: README.md
authors:
- "Julien Riou (https://git.riou.xyz/jriou)"
description: "My Ansible Collection"
license_file: LICENSE
tags:
- infrastructure
- certbot
- coller
- firefly
- forgejo
- forgejo_runners
- galene
- golang
- navidrome
repository: "https://git.riou.xyz/jriou/ansible"
issues: "https://git.riou.xyz/jriou/ansible/issues"
build_ignore:
- .github
- .gitignore
- changelogs
- roles/*/molecule
- '*.tar.gz'
- test-requirements.txt
- test-requirements.yml

61
roles/certbot/README.md Normal file
View file

@ -0,0 +1,61 @@
# Ansible Role Certbot
## Table of content
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`certbot_email`](#variable-certbot_email)
* [`certbot_domain`](#variable-certbot_domain)
* [`certbot_molecule`](#variable-certbot_molecule)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `certbot_email` | `str` | Yes | N/A | E-mail to register the certificate. |
| `certbot_domain` | `str` | Yes | N/A | Domain name to register the certificate. |
| `certbot_molecule` | `bool` | No | `false` | Run the role with Ansible Molecule.<br><br>Disable cert generation in the CI. |
### `certbot_email`<a id="variable-certbot_email"></a>
[*⇑ Back to ToC ⇑*](#toc)
E-mail to register the certificate.
- **Type**: `str`
- **Required**: Yes
### `certbot_domain`<a id="variable-certbot_domain"></a>
[*⇑ Back to ToC ⇑*](#toc)
Domain name to register the certificate.
- **Type**: `str`
- **Required**: Yes
### `certbot_molecule`<a id="variable-certbot_molecule"></a>
[*⇑ Back to ToC ⇑*](#toc)
Run the role with Ansible Molecule.
Disable cert generation in the CI.
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
<!-- ANSIBLE DOCSMITH MAIN END -->

View file

@ -0,0 +1,10 @@
---
# Run the role with Ansible Molecule.
#
# Disable cert generation in the CI.
#
# - Type: bool
# - Required: No
# - Default: false
certbot_molecule: false

View file

@ -0,0 +1,25 @@
---
argument_specs:
main:
short_description: Install and configure a certbot
description:
- Install and configure a [certbot](https://certbot.eff.org/).
author:
- jriou
options:
certbot_email:
description:
- E-mail to register the certificate.
required: true
certbot_domain:
description:
- Domain name to register the certificate.
required: true
certbot_molecule:
description:
- Run the role with Ansible Molecule.
- Disable cert generation in the CI.
type: bool
default: false

View file

@ -0,0 +1,9 @@
---
- name: Converge
hosts: molecule
roles:
- certbot
vars:
certbot_domain: test.org
certbot_email: test@test.org
certbot_molecule: true

View file

@ -0,0 +1,18 @@
---
- name: Create containers
hosts: localhost
gather_facts: false
tasks:
- name: Create containers
containers.podman.podman_container:
hostname: "{{ item }}"
name: "{{ item }}"
image: "{{ hostvars[item]['container_image'] }}"
state: started
loop: "{{ groups['molecule'] }}"
- name: Wait for containers to be ready
ansible.builtin.wait_for_connection:
timeout: 300
delegate_to: "{{ item }}"
loop: "{{ groups['molecule'] }}"

View file

@ -0,0 +1,11 @@
---
- name: Destroy container instances
hosts: localhost
gather_facts: false
tasks:
- name: Remove containers
containers.podman.podman_container:
name: "{{ item }}"
state: absent
loop: "{{ groups['molecule'] }}"
failed_when: false

View file

@ -0,0 +1,12 @@
---
molecule:
hosts:
debian11:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian11-ansible:latest
debian12:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian12-ansible:latest
debian13:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian13-ansible:latest

View file

@ -0,0 +1,24 @@
---
ansible:
executor:
args:
ansible_playbook:
- --inventory=inventory/
env:
ANSIBLE_ROLES_PATH: ../../../../roles
playbooks:
create: create.yml
destroy: destroy.yml
converge: converge.yml
dependency:
name: galaxy
options:
requirements-file: ${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml
scenario:
test_sequence:
- create
- converge
- idempotence
- destroy

View file

@ -0,0 +1,3 @@
---
collections:
- name: containers.podman

View file

@ -0,0 +1,13 @@
---
- name: Install packages
ansible.builtin.apt:
name: certbot
update_cache: true
- 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
when: not certbot_molecule

159
roles/coller/README.md Normal file
View file

@ -0,0 +1,159 @@
# Ansible Role Coller
Ansible role to manage a [coller](https://git.riou.xyz/jriou/coller) 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 `coller_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
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`coller_version`](#variable-coller_version)
* [`coller_config_dir`](#variable-coller_config_dir)
* [`coller_port`](#variable-coller_port)
* [`coller_manage_iptables`](#variable-coller_manage_iptables)
* [`coller_allowed_sources`](#variable-coller_allowed_sources)
* [`coller_db_name`](#variable-coller_db_name)
* [`coller_db_user`](#variable-coller_db_user)
* [`coller_db_password`](#variable-coller_db_password)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `coller_version` | `str` | No | `"1.3.1"` | Version of the binary. |
| `coller_config_dir` | `path` | No | `"/etc/coller"` | Directory of the configuration files. |
| `coller_port` | `int` | No | `8080` | Port to listen. |
| `coller_manage_iptables` | `bool` | No | `false` | Create iptables rule to allow the service. |
| `coller_allowed_sources` | `list` | No | N/A | List of allowed networks to allow.<br><br>Enabled when `coller_manage_iptables` is enabled. |
| `coller_db_name` | `str` | No | `"coller"` | Name of the database to connect. |
| `coller_db_user` | `str` | No | `"coller"` | User to connect to the database. |
| `coller_db_password` | `str` | Yes | N/A | Password to connect to the database. |
### `coller_version`<a id="variable-coller_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version of the binary.
- **Type**: `str`
- **Required**: No
- **Default**: `"1.3.1"`
### `coller_config_dir`<a id="variable-coller_config_dir"></a>
[*⇑ Back to ToC ⇑*](#toc)
Directory of the configuration files.
- **Type**: `path`
- **Required**: No
- **Default**: `"/etc/coller"`
### `coller_port`<a id="variable-coller_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
Port to listen.
- **Type**: `int`
- **Required**: No
- **Default**: `8080`
### `coller_manage_iptables`<a id="variable-coller_manage_iptables"></a>
[*⇑ Back to ToC ⇑*](#toc)
Create iptables rule to allow the service.
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
### `coller_allowed_sources`<a id="variable-coller_allowed_sources"></a>
[*⇑ Back to ToC ⇑*](#toc)
List of allowed networks to allow.
Enabled when `coller_manage_iptables` is enabled.
- **Type**: `list`
- **Required**: No
### `coller_db_name`<a id="variable-coller_db_name"></a>
[*⇑ Back to ToC ⇑*](#toc)
Name of the database to connect.
- **Type**: `str`
- **Required**: No
- **Default**: `"coller"`
### `coller_db_user`<a id="variable-coller_db_user"></a>
[*⇑ Back to ToC ⇑*](#toc)
User to connect to the database.
- **Type**: `str`
- **Required**: No
- **Default**: `"coller"`
### `coller_db_password`<a id="variable-coller_db_password"></a>
[*⇑ Back to ToC ⇑*](#toc)
Password to connect to the database.
- **Type**: `str`
- **Required**: Yes
<!-- ANSIBLE DOCSMITH MAIN END -->
## Usage
Example of a basic coller.yml playbook:
```yaml
- hosts: coller
roles:
- coller
```
Then run the playbook:
```
ansible-playbook coller.yml
```

View file

@ -0,0 +1,51 @@
---
# Version of the binary.
#
# - Type: str
# - Required: No
# - Default: 1.3.1
coller_version: 1.3.1
# Directory of the configuration files.
#
# - Type: path
# - Required: No
# - Default: /etc/coller
coller_config_dir: /etc/coller
# Port to listen.
#
# - Type: int
# - Required: No
# - Default: 8080
coller_port: 8080
# Create iptables rule to allow the service.
#
# - Type: bool
# - Required: No
# - Default: false
coller_manage_iptables: false
# List of allowed networks to allow.
#
# Enabled when `coller_manage_iptables` is enabled.
#
# - Type: list
# - Required: No
coller_allowed_sources: []
# Name of the database to connect.
#
# - Type: str
# - Required: No
# - Default: coller
coller_db_name: coller
# User to connect to the database.
#
# - Type: str
# - Required: No
# - Default: coller
coller_db_user: coller

View file

@ -0,0 +1,4 @@
---
- name: save iptables
ansible.builtin.shell:
cmd: netfilter-persistent save

View file

@ -0,0 +1,52 @@
---
argument_specs:
main:
short_description: Install and configure a coller instance
description:
- Install and configure a [coller](https://git.riou.xyz/jriou/coller) instance.
author:
- jriou
options:
coller_version:
description:
- Version of the binary.
default: "1.3.1"
coller_config_dir:
description:
- Directory of the configuration files.
type: path
default: /etc/coller
coller_port:
description:
- Port to listen.
type: int
default: 8080
coller_manage_iptables:
description:
- Create iptables rule to allow the service.
type: bool
default: false
coller_allowed_sources:
description:
- List of allowed networks to allow.
- Enabled when `coller_manage_iptables` is enabled.
type: list
coller_db_name:
description:
- Name of the database to connect.
default: coller
coller_db_user:
description:
- User to connect to the database.
default: coller
coller_db_password:
description:
- Password to connect to the database.
required: true

View file

@ -0,0 +1,3 @@
---
dependencies:
- role: geerlingguy.docker

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

234
roles/firefly/README.md Normal file
View file

@ -0,0 +1,234 @@
# Ansible Role Firefly
Ansible role to manage a [Firefly III](https://firefly-iii.org/) instance.
## Table of content
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`firefly_version`](#variable-firefly_version)
* [`firefly_port`](#variable-firefly_port)
* [`firefly_static_cron_token`](#variable-firefly_static_cron_token)
* [`firefly_home`](#variable-firefly_home)
* [`firefly_site_owner`](#variable-firefly_site_owner)
* [`firefly_app_key`](#variable-firefly_app_key)
* [`firefly_language`](#variable-firefly_language)
* [`firefly_tz`](#variable-firefly_tz)
* [`firefly_db_database`](#variable-firefly_db_database)
* [`firefly_db_username`](#variable-firefly_db_username)
* [`firefly_db_password`](#variable-firefly_db_password)
* [`firefly_manage_iptables`](#variable-firefly_manage_iptables)
* [`firefly_allowed_sources`](#variable-firefly_allowed_sources)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `firefly_version` | `str` | No | `"latest"` | Version of the docker image. |
| `firefly_port` | `int` | No | `8080` | |
| `firefly_static_cron_token` | `str` | Yes | N/A | Token used by the cron job (sensitive). |
| `firefly_home` | `path` | No | `"/var/lib/firefly"` | Directory where to store data files. |
| `firefly_site_owner` | `str` | No | `"root@localhost"` | E-mail address of the site owner. |
| `firefly_app_key` | `str` | Yes | N/A | Application key (sensitive). |
| `firefly_language` | `str` | No | `"en_US"` | Language of the web interface. |
| `firefly_tz` | `str` | No | `"Etc/UTC"` | Time zone of the web interface. |
| `firefly_db_database` | `str` | No | `"firefly"` | Name of the database. |
| `firefly_db_username` | `str` | No | `"firefly"` | Name of the user to connect to the database. |
| `firefly_db_password` | `str` | Yes | N/A | Password to connect to the database (sensitive). |
| `firefly_manage_iptables` | `bool` | No | `false` | Configure iptables rules. |
| `firefly_allowed_sources` | `list` | No | N/A | List of IP ranges to allow when `firefly_manage_iptables` is enabled. |
### `firefly_version`<a id="variable-firefly_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version of the docker image.
- **Type**: `str`
- **Required**: No
- **Default**: `"latest"`
### `firefly_port`<a id="variable-firefly_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
- **Type**: `int`
- **Required**: No
- **Default**: `8080`
### `firefly_static_cron_token`<a id="variable-firefly_static_cron_token"></a>
[*⇑ Back to ToC ⇑*](#toc)
Token used by the cron job (sensitive).
- **Type**: `str`
- **Required**: Yes
### `firefly_home`<a id="variable-firefly_home"></a>
[*⇑ Back to ToC ⇑*](#toc)
Directory where to store data files.
- **Type**: `path`
- **Required**: No
- **Default**: `"/var/lib/firefly"`
### `firefly_site_owner`<a id="variable-firefly_site_owner"></a>
[*⇑ Back to ToC ⇑*](#toc)
E-mail address of the site owner.
- **Type**: `str`
- **Required**: No
- **Default**: `"root@localhost"`
### `firefly_app_key`<a id="variable-firefly_app_key"></a>
[*⇑ Back to ToC ⇑*](#toc)
Application key (sensitive).
- **Type**: `str`
- **Required**: Yes
### `firefly_language`<a id="variable-firefly_language"></a>
[*⇑ Back to ToC ⇑*](#toc)
Language of the web interface.
- **Type**: `str`
- **Required**: No
- **Default**: `"en_US"`
### `firefly_tz`<a id="variable-firefly_tz"></a>
[*⇑ Back to ToC ⇑*](#toc)
Time zone of the web interface.
- **Type**: `str`
- **Required**: No
- **Default**: `"Etc/UTC"`
### `firefly_db_database`<a id="variable-firefly_db_database"></a>
[*⇑ Back to ToC ⇑*](#toc)
Name of the database.
- **Type**: `str`
- **Required**: No
- **Default**: `"firefly"`
### `firefly_db_username`<a id="variable-firefly_db_username"></a>
[*⇑ Back to ToC ⇑*](#toc)
Name of the user to connect to the database.
- **Type**: `str`
- **Required**: No
- **Default**: `"firefly"`
### `firefly_db_password`<a id="variable-firefly_db_password"></a>
[*⇑ Back to ToC ⇑*](#toc)
Password to connect to the database (sensitive).
- **Type**: `str`
- **Required**: Yes
### `firefly_manage_iptables`<a id="variable-firefly_manage_iptables"></a>
[*⇑ Back to ToC ⇑*](#toc)
Configure iptables rules.
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
### `firefly_allowed_sources`<a id="variable-firefly_allowed_sources"></a>
[*⇑ Back to ToC ⇑*](#toc)
List of IP ranges to allow when `firefly_manage_iptables` is enabled.
- **Type**: `list`
- **Required**: No
<!-- ANSIBLE DOCSMITH MAIN END -->
## 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).

View file

@ -0,0 +1,66 @@
---
firefly_port: 8000
# Version of the docker image.
#
# - Type: str
# - Required: No
# - Default: latest
firefly_version: latest
# Directory where to store data files.
#
# - Type: path
# - Required: No
# - Default: /var/lib/firefly
firefly_home: /var/lib/firefly
# E-mail address of the site owner.
#
# - Type: str
# - Required: No
# - Default: root@localhost
firefly_site_owner: root@localhost
# Language of the web interface.
#
# - Type: str
# - Required: No
# - Default: en_US
firefly_language: en_US
# Time zone of the web interface.
#
# - Type: str
# - Required: No
# - Default: Etc/UTC
firefly_tz: Etc/UTC
# Name of the database.
#
# - Type: str
# - Required: No
# - Default: firefly
firefly_db_database: firefly
# Name of the user to connect to the database.
#
# - Type: str
# - Required: No
# - Default: firefly
firefly_db_username: firefly
# Configure iptables rules.
#
# - Type: bool
# - Required: No
# - Default: false
firefly_manage_iptables: false
# List of IP ranges to allow when `firefly_manage_iptables` is enabled.
#
# - Type: list
# - Required: No
firefly_allowed_sources: []

View file

@ -0,0 +1,4 @@
---
- name: save iptables
ansible.builtin.shell:
cmd: netfilter-persistent save

View file

@ -0,0 +1,76 @@
---
argument_specs:
main:
short_description: Install and configure Firefly III
description:
- Install and configure [Firefly III](https://www.firefly-iii.org/).
author:
- jriou
options:
firefly_version:
description:
- Version of the docker image.
default: latest
firefly_port:
descritpion:
- Port to listen.
type: int
default: 8080
firefly_static_cron_token:
description:
- Token used by the cron job (sensitive).
required: true
firefly_home:
description:
- Directory where to store data files.
type: path
default: /var/lib/firefly
firefly_site_owner:
description:
- E-mail address of the site owner.
default: root@localhost
firefly_app_key:
description:
- Application key (sensitive).
required: true
firefly_language:
description:
- Language of the web interface.
default: en_US
firefly_tz:
description:
- Time zone of the web interface.
default: Etc/UTC
firefly_db_database:
description:
- Name of the database.
default: firefly
firefly_db_username:
description:
- Name of the user to connect to the database.
default: firefly
firefly_db_password:
description:
- Password to connect to the database (sensitive).
required: true
firefly_manage_iptables:
description:
- Configure iptables rules.
type: bool
default: false
firefly_allowed_sources:
description:
- List of IP ranges to allow when `firefly_manage_iptables` is enabled.
type: list

View file

@ -0,0 +1,3 @@
---
dependencies:
- role: geerlingguy.docker

View file

@ -0,0 +1,47 @@
---
- name: check requirements
ansible.builtin.assert:
that:
- firefly_static_cron_token is defined
- firefly_db_password is defined
- firefly_app_key is defined
- 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

View file

@ -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

View file

@ -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

View file

@ -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

239
roles/forgejo/README.md Normal file
View file

@ -0,0 +1,239 @@
# 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
<!-- ANSIBLE DOCSMITH TOC START -->
* [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)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
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<br><br>Define a `enabled` key with a boolean to enable the mailer<br><br>Define a `from` key with the source e-mail address<br><br>See [Email setup](https://forgejo.org/docs/latest/admin/setup/email/) |
| `forgejo_service` | `dict` | No | N/A | Configure service settings<br><br>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_server`<a id="variable-forgejo_server"></a>
[*⇑ Back to ToC ⇑*](#toc)
Enable the server mode
- **Type**: `bool`
- **Required**: No
- **Default**: `true`
### `forgejo_version`<a id="variable-forgejo_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version of the Forgejo binaries
- **Type**: `int`
- **Required**: No
- **Default**: `14`
### `forgejo_home_dir`<a id="variable-forgejo_home_dir"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the home directory
- **Type**: `path`
- **Required**: No
- **Default**: `"/var/lib/forgejo"`
### `forgejo_config_dir`<a id="variable-forgejo_config_dir"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the configuration directory
- **Type**: `path`
- **Required**: No
- **Default**: `"/etc/forgejo"`
### `forgejo_web_port`<a id="variable-forgejo_web_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
Port to listen for the web UI
- **Type**: `int`
- **Required**: No
- **Default**: `3000`
### `forgejo_ssh_port`<a id="variable-forgejo_ssh_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
Port to listen for SSH
- **Type**: `int`
- **Required**: No
- **Default**: `222`
### `forgejo_db_username`<a id="variable-forgejo_db_username"></a>
[*⇑ Back to ToC ⇑*](#toc)
Name of the user in the database
- **Type**: `str`
- **Required**: No
- **Default**: `"forgejo"`
### `forgejo_db_password`<a id="variable-forgejo_db_password"></a>
[*⇑ Back to ToC ⇑*](#toc)
Password of the user in the database
- **Type**: `str`
- **Required**: Yes
### `forgejo_db_database`<a id="variable-forgejo_db_database"></a>
[*⇑ Back to ToC ⇑*](#toc)
Name of the database
- **Type**: `str`
- **Required**: No
- **Default**: `"forgejo"`
### `forgejo_mailer`<a id="variable-forgejo_mailer"></a>
[*⇑ 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`<a id="variable-forgejo_service"></a>
[*⇑ 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`<a id="variable-forgejo_manage_iptables"></a>
[*⇑ Back to ToC ⇑*](#toc)
Configure iptables rules
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
### `forgejo_allowed_sources`<a id="variable-forgejo_allowed_sources"></a>
[*⇑ Back to ToC ⇑*](#toc)
List of IP ranges to allow when `forgejo_manage_iptables` is enabled
- **Type**: `list`
- **Required**: No
<!-- ANSIBLE DOCSMITH MAIN END -->
## Usage
Example of a basic forgejo.yml playbook:
```yaml
- hosts: forgejo
roles:
- forgejo
```
Then run the playbook:
```
ansible-playbook forgejo.yml
```
## Donate
As we all love FOSS projects, you should consider [donating to
Codeberg](https://donate.codeberg.org/), the non-profit organization behind
Forgejo.

View file

@ -0,0 +1,90 @@
---
# 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
# 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: []

View file

@ -0,0 +1,4 @@
---
- name: Save iptables
ansible.builtin.shell:
cmd: netfilter-persistent save

View file

@ -0,0 +1,84 @@
---
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

View file

@ -0,0 +1,3 @@
---
dependencies:
- role: geerlingguy.docker

View file

@ -0,0 +1,64 @@
---
- name: Check database password
ansible.builtin.assert:
that: forgejo_db_password is defined
- name: Add forgejo user
ansible.builtin.user:
name: forgejo
system: yes
password: '!'
home: "{{ forgejo_home_dir }}"
create_home: false
- name: Read forgejo attributes
ansible.builtin.getent:
database: passwd
key: forgejo
- 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 }}"

View file

@ -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

View file

@ -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

View file

@ -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 %}

View file

@ -0,0 +1,118 @@
# Ansible Role Forgejo Runners
Ansible role to manage [Forgejo](https://forgejo.org/) runners.
## 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
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`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_settings`](#variable-forgejo_runners_settings)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `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_settings` | `dict` | No | N/A | Dict of runners to configure<br><br>The key is the name of the repository on the instance<br><br>The value is a dict with a `token` key and optionally a dict of `labels` |
### `forgejo_runners_version`<a id="variable-forgejo_runners_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version of the runners
- **Type**: `str`
- **Required**: No
- **Default**: `"9.1.1"`
### `forgejo_runners_config_dir`<a id="variable-forgejo_runners_config_dir"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the configuration directory of the runners
- **Type**: `path`
- **Required**: No
- **Default**: `"/etc/forgejo-runners"`
### `forgejo_runners_instance`<a id="variable-forgejo_runners_instance"></a>
[*⇑ Back to ToC ⇑*](#toc)
URL of the Forgejo instance to register the runners
- **Type**: `str`
- **Required**: No
### `forgejo_runners_settings`<a id="variable-forgejo_runners_settings"></a>
[*⇑ Back to ToC ⇑*](#toc)
Dict 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
<!-- ANSIBLE DOCSMITH MAIN END -->
## Usage
Example of a basic playbook:
```yaml
- hosts: forgejo_runners
roles:
- forgejo_runners
vars:
forgejo_runners_instance: https://codeberg.org # FIXME
forgejo_runners:
my_runner:
token: **redacted**
labels:
node-latest: docker://data.forgejo.org/oci/node:latest
```
Then run the playbook:
```
ansible-playbook forgejo_runners.yml
```
## Donate
As we all love FOSS projects, you should consider [donating to
Codeberg](https://donate.codeberg.org/), the non-profit organization behind
Forgejo.

View file

@ -0,0 +1 @@
Focus on runners only

View file

@ -0,0 +1,25 @@
---
# 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
# Dict 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_settings: {}

View file

@ -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

View file

@ -0,0 +1,30 @@
---
argument_specs:
main:
short_description: Install and configure Forgejo runners
description:
- Install and configure [Forgejo](https://forgejo.org/) runners.
author:
- jriou
options:
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_settings:
description:
- Dict 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

View file

@ -0,0 +1,3 @@
---
dependencies:
- role: geerlingguy.docker

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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:

View file

@ -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:

View file

@ -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 %}

213
roles/galene/README.md Normal file
View file

@ -0,0 +1,213 @@
# Ansible Role Galene
Install and configure [Galene](https://galene.org) videoconference server.
## Table of content
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`galene_version`](#variable-galene_version)
* [`galene_http_port`](#variable-galene_http_port)
* [`galene_turn`](#variable-galene_turn)
* [`galene_user`](#variable-galene_user)
* [`galene_group`](#variable-galene_group)
* [`galene_base_directory`](#variable-galene_base_directory)
* [`galene_data_directory`](#variable-galene_data_directory)
* [`galene_groups_directory`](#variable-galene_groups_directory)
* [`galene_recording_directory`](#variable-galene_recording_directory)
* [`galene_static_directory`](#variable-galene_static_directory)
* [`galene_domain`](#variable-galene_domain)
* [`galene_config`](#variable-galene_config)
* [`galene_groups`](#variable-galene_groups)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `galene_version` | `str` | No | `"galene-1.0"` | Reference (branch or tag) of the Galene git repository. |
| `galene_http_port` | `int` | No | `443` | Port to listen. |
| `galene_turn` | `str` | No | `":1194"` | TURN address. |
| `galene_user` | `str` | No | `"galene"` | Operating system user to run the service. |
| `galene_group` | `str` | No | `"galene"` | Operating system group to run the service. |
| `galene_base_directory` | `path` | No | `"/var/lib/galene"` | Path to the base directory. |
| `galene_data_directory` | `path` | No | `"{{ galene_base_directory }}/data"` | Path to the data directory. |
| `galene_groups_directory` | `path` | No | `"{{ galene_base_directory }}/groups"` | Path to the groups directory. |
| `galene_recording_directory` | `path` | No | `"{{ galene_base_directory }}/recordings"` | Path to the recordings directory. |
| `galene_static_directory` | `path` | No | `"{{ galene_base_directory }}/static"` | Path to the static directory. |
| `galene_domain` | `str` | No | N/A | Domain name.<br><br>Used to generate TLS certificates. |
| `galene_config` | `dict` | No | N/A | Custom settings.<br><br>Key is the name of the setting.<br><br>Value is the value of the setting.<br><br>See [The global configuration file](https://galene.org/galene.html#the-global-configuration-file). |
| `galene_groups` | `dict` | No | N/A | Dict of groups.<br><br>Key is the group name.<br><br>Value is the group definition.<br><br>See [Group definitions](https://galene.org/galene.html#group-definitions). |
### `galene_version`<a id="variable-galene_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Reference (branch or tag) of the Galene git repository.
- **Type**: `str`
- **Required**: No
- **Default**: `"galene-1.0"`
### `galene_http_port`<a id="variable-galene_http_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
Port to listen.
- **Type**: `int`
- **Required**: No
- **Default**: `443`
### `galene_turn`<a id="variable-galene_turn"></a>
[*⇑ Back to ToC ⇑*](#toc)
TURN address.
- **Type**: `str`
- **Required**: No
- **Default**: `":1194"`
### `galene_user`<a id="variable-galene_user"></a>
[*⇑ Back to ToC ⇑*](#toc)
Operating system user to run the service.
- **Type**: `str`
- **Required**: No
- **Default**: `"galene"`
### `galene_group`<a id="variable-galene_group"></a>
[*⇑ Back to ToC ⇑*](#toc)
Operating system group to run the service.
- **Type**: `str`
- **Required**: No
- **Default**: `"galene"`
### `galene_base_directory`<a id="variable-galene_base_directory"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the base directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"/var/lib/galene"`
### `galene_data_directory`<a id="variable-galene_data_directory"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the data directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"{{ galene_base_directory }}/data"`
### `galene_groups_directory`<a id="variable-galene_groups_directory"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the groups directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"{{ galene_base_directory }}/groups"`
### `galene_recording_directory`<a id="variable-galene_recording_directory"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the recordings directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"{{ galene_base_directory }}/recordings"`
### `galene_static_directory`<a id="variable-galene_static_directory"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the static directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"{{ galene_base_directory }}/static"`
### `galene_domain`<a id="variable-galene_domain"></a>
[*⇑ Back to ToC ⇑*](#toc)
Domain name.
Used to generate TLS certificates.
- **Type**: `str`
- **Required**: No
### `galene_config`<a id="variable-galene_config"></a>
[*⇑ Back to ToC ⇑*](#toc)
Custom settings.
Key is the name of the setting.
Value is the value of the setting.
See [The global configuration file](https://galene.org/galene.html#the-global-configuration-file).
- **Type**: `dict`
- **Required**: No
### `galene_groups`<a id="variable-galene_groups"></a>
[*⇑ Back to ToC ⇑*](#toc)
Dict of groups.
Key is the group name.
Value is the group definition.
See [Group definitions](https://galene.org/galene.html#group-definitions).
- **Type**: `dict`
- **Required**: No
<!-- ANSIBLE DOCSMITH MAIN END -->

View file

@ -0,0 +1,95 @@
---
# Reference (branch or tag) of the Galene git repository.
#
# - Type: str
# - Required: No
# - Default: galene-1.0
galene_version: galene-1.0
# Port to listen.
#
# - Type: int
# - Required: No
# - Default: 443
galene_http_port: 443
# TURN address.
#
# - Type: str
# - Required: No
# - Default: :1194
galene_turn: ":1194"
# Operating system user to run the service.
#
# - Type: str
# - Required: No
# - Default: galene
galene_user: galene
# Operating system group to run the service.
#
# - Type: str
# - Required: No
# - Default: galene
galene_group: galene
# Path to the base directory.
#
# - Type: path
# - Required: No
# - Default: /var/lib/galene
galene_base_directory: /var/lib/galene
# Path to the data directory.
#
# - Type: path
# - Required: No
# - Default: {{ galene_base_directory }}/data
galene_data_directory: "{{ galene_base_directory }}/data"
# Path to the groups directory.
#
# - Type: path
# - Required: No
# - Default: {{ galene_base_directory }}/groups
galene_groups_directory: "{{ galene_base_directory }}/groups"
# Path to the recordings directory.
#
# - Type: path
# - Required: No
# - Default: {{ galene_base_directory }}/recordings
galene_recording_directory: "{{ galene_base_directory }}/recordings"
# Path to the static directory.
#
# - Type: path
# - Required: No
# - Default: {{ galene_base_directory }}/static
galene_static_directory: "{{ galene_base_directory }}/static"
# Custom settings.
#
# Key is the name of the setting.
#
# Value is the value of the setting.
#
# See .
#
# - Type: dict
# - Required: No
galene_config: {}
# Dict of groups.
#
# Key is the group name.
#
# Value is the group definition.
#
# See .
#
# - Type: dict
# - Required: No
galene_groups: {}

View file

@ -0,0 +1,9 @@
---
- name: Reload systemd
ansible.builtin.systemd_service:
daemon_reload: true
- name: Restart galene
ansible.builtin.service:
name: galene
state: restarted

View file

@ -0,0 +1,85 @@
---
argument_specs:
main:
short_description: Install and configure Galene videoconference server
description:
- Install and configure [Galene](https://galene.org/) videoconference server
author:
- jriou
options:
galene_version:
description:
- Reference (branch or tag) of the Galene git repository.
default: galene-1.0
galene_http_port:
description:
- Port to listen.
type: int
default: 443
galene_turn:
description:
- TURN address.
default: ":1194"
galene_user:
description:
- Operating system user to run the service.
default: galene
galene_group:
description:
- Operating system group to run the service.
default: galene
galene_base_directory:
description:
- Path to the base directory.
type: path
default: /var/lib/galene
galene_data_directory:
description:
- Path to the data directory.
type: path
default: "{{ galene_base_directory }}/data"
galene_groups_directory:
description:
- Path to the groups directory.
type: path
default: "{{ galene_base_directory }}/groups"
galene_recording_directory:
description:
- Path to the recordings directory.
type: path
default: "{{ galene_base_directory }}/recordings"
galene_static_directory:
description:
- Path to the static directory.
type: path
default: "{{ galene_base_directory }}/static"
galene_domain:
description:
- Domain name.
- Used to generate TLS certificates.
galene_config:
description:
- Custom settings.
- Key is the name of the setting.
- Value is the value of the setting.
- See [The global configuration file](https://galene.org/galene.html#the-global-configuration-file).
type: dict
galene_groups:
description:
- Dict of groups.
- Key is the group name.
- Value is the group definition.
- See [Group definitions](https://galene.org/galene.html#group-definitions).
type: dict

View file

@ -0,0 +1,3 @@
---
dependencies:
- role: golang

View file

@ -0,0 +1,5 @@
---
- name: Converge
hosts: molecule
roles:
- galene

View file

@ -0,0 +1,18 @@
---
- name: Create containers
hosts: localhost
gather_facts: false
tasks:
- name: Create containers
containers.podman.podman_container:
hostname: "{{ item }}"
name: "{{ item }}"
image: "{{ hostvars[item]['container_image'] }}"
state: started
loop: "{{ groups['molecule'] }}"
- name: Wait for containers to be ready
ansible.builtin.wait_for_connection:
timeout: 300
delegate_to: "{{ item }}"
loop: "{{ groups['molecule'] }}"

View file

@ -0,0 +1,11 @@
---
- name: Destroy container instances
hosts: localhost
gather_facts: false
tasks:
- name: Remove containers
containers.podman.podman_container:
name: "{{ item }}"
state: absent
loop: "{{ groups['molecule'] }}"
failed_when: false

View file

@ -0,0 +1,12 @@
---
molecule:
hosts:
debian11:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian11-ansible:latest
debian12:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian12-ansible:latest
debian13:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian13-ansible:latest

View file

@ -0,0 +1,26 @@
---
ansible:
executor:
args:
ansible_playbook:
- --inventory=inventory/
env:
ANSIBLE_ROLES_PATH: ../../../../roles
playbooks:
create: create.yml
converge: converge.yml
verify: verify.yml
destroy: destroy.yml
dependency:
name: galaxy
options:
requirements-file: ${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml
scenario:
test_sequence:
- create
- converge
- idempotence
- verify
- destroy

View file

@ -0,0 +1,3 @@
---
collections:
- name: containers.podman

View file

@ -0,0 +1,8 @@
---
- name: Verify
hosts: molecule
tasks:
- name: Check service
ansible.builtin.uri:
url: https://localhost:443
validate_certs: false

117
roles/galene/tasks/main.yml Normal file
View file

@ -0,0 +1,117 @@
---
# TODO: install in block
- name: Install requirements
ansible.builtin.apt:
name: git
update_cache: true
- 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

View file

@ -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

34
roles/golang/README.md Normal file
View file

@ -0,0 +1,34 @@
# Ansible Role Go
Install [Go](https://go.dev/).
## Table of content
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`golang_version`](#variable-golang_version)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `golang_version` | `str` | No | `"1.25.4"` | Version to install. |
### `golang_version`<a id="variable-golang_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version to install.
- **Type**: `str`
- **Required**: No
- **Default**: `"1.25.4"`
<!-- ANSIBLE DOCSMITH MAIN END -->

View file

@ -0,0 +1,8 @@
---
# Version to install.
#
# - Type: str
# - Required: No
# - Default: 1.25.4
golang_version: 1.25.4

View file

@ -0,0 +1,13 @@
---
argument_specs:
main:
short_description: Install Go
description:
- Install [Go](https://go.dev/).
author:
- jriou
options:
golang_version:
description:
- Version to install.
default: 1.25.4

View file

@ -0,0 +1,7 @@
---
- name: Converge
hosts: molecule
roles:
- golang
vars:
golang_version: 1.25.4

View file

@ -0,0 +1,18 @@
---
- name: Create containers
hosts: localhost
gather_facts: false
tasks:
- name: Create containers
containers.podman.podman_container:
hostname: "{{ item }}"
name: "{{ item }}"
image: "{{ hostvars[item]['container_image'] }}"
state: started
loop: "{{ groups['molecule'] }}"
- name: Wait for containers to be ready
ansible.builtin.wait_for_connection:
timeout: 300
delegate_to: "{{ item }}"
loop: "{{ groups['molecule'] }}"

View file

@ -0,0 +1,11 @@
---
- name: Destroy container instances
hosts: localhost
gather_facts: false
tasks:
- name: Remove containers
containers.podman.podman_container:
name: "{{ item }}"
state: absent
loop: "{{ groups['molecule'] }}"
failed_when: false

View file

@ -0,0 +1,12 @@
---
molecule:
hosts:
debian11:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian11-ansible:latest
debian12:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian12-ansible:latest
debian13:
ansible_connection: containers.podman.podman
container_image: docker.io/geerlingguy/docker-debian13-ansible:latest

View file

@ -0,0 +1,26 @@
---
ansible:
executor:
args:
ansible_playbook:
- --inventory=inventory/
env:
ANSIBLE_ROLES_PATH: ../../../../roles
playbooks:
create: create.yml
converge: converge.yml
verify: verify.yml
destroy: destroy.yml
dependency:
name: galaxy
options:
requirements-file: ${MOLECULE_SCENARIO_DIRECTORY}/requirements.yml
scenario:
test_sequence:
- create
- converge
- idempotence
- verify
- destroy

View file

@ -0,0 +1,3 @@
---
collections:
- name: containers.podman

View file

@ -0,0 +1,15 @@
---
- name: Verify
hosts: molecule
vars:
golang_version: 1.25.4
tasks:
- name: Get version
ansible.builtin.command:
cmd: /usr/local/go/bin/go version
register: golang_version_cmd
- name: Compare versions
ansible.builtin.assert:
that:
- golang_version_cmd.stdout | regex_search('^go version go' + golang_version + ' linux/amd64') != ""

View file

@ -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

1
roles/navidrome/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
venv

205
roles/navidrome/README.md Normal file
View file

@ -0,0 +1,205 @@
# Ansible Role Navidrome
Ansible role to manage a [Navidrome](https://github.com/navidrome/navidrome) instance.
## Table of Content
<!-- ANSIBLE DOCSMITH TOC START -->
* [Role variables](#variables)
* [`navidrome_version`](#variable-navidrome_version)
* [`navidrome_arch`](#variable-navidrome_arch)
* [`navidrome_user`](#variable-navidrome_user)
* [`navidrome_group`](#variable-navidrome_group)
* [`navidrome_music_folder`](#variable-navidrome_music_folder)
* [`navidrome_data_folder`](#variable-navidrome_data_folder)
* [`navidrome_cache_folder`](#variable-navidrome_cache_folder)
* [`navidrome_manage_iptables`](#variable-navidrome_manage_iptables)
* [`navidrome_allowed_sources`](#variable-navidrome_allowed_sources)
* [`navidrome_address`](#variable-navidrome_address)
* [`navidrome_port`](#variable-navidrome_port)
* [`navidrome_enable_insights_collector`](#variable-navidrome_enable_insights_collector)
* [`navidrome_base_url`](#variable-navidrome_base_url)
<!-- ANSIBLE DOCSMITH TOC END -->
<!-- ANSIBLE DOCSMITH MAIN START -->
## Role variables<a id="variables"></a>
The following variables can be configured for this role:
| Variable | Type | Required | Default | Description (abstract) |
|----------|------|----------|---------|------------------------|
| `navidrome_version` | `str` | No | `"0.54.5"` | Version of the debian package. |
| `navidrome_arch` | `str` | No | `"amd64"` | Architecture of the debian package. |
| `navidrome_user` | `str` | No | `"navidrome"` | Operating system user to run the service. |
| `navidrome_group` | `str` | No | `"navidrome"` | Operating system group to run the service. |
| `navidrome_music_folder` | `path` | No | `"/opt/navidrome/music"` | Path to the music folder. |
| `navidrome_data_folder` | `path` | No | `"/var/lib/navidrome"` | Path to the data directory. |
| `navidrome_cache_folder` | `path` | No | `"{{ navidrome_data_folder }}/cache"` | Path to the cache folder. |
| `navidrome_manage_iptables` | `bool` | No | `false` | Configure iptables rules |
| `navidrome_allowed_sources` | `list` | No | N/A | List of IP ranges to allow when `navidrome_manage_iptables` is enabled |
| `navidrome_address` | `str` | No | `"localhost"` | Address to listen. |
| `navidrome_port` | `int` | No | `4533` | Port to listen |
| `navidrome_enable_insights_collector` | `bool` | No | `false` | Enable the insights collector. |
| `navidrome_base_url` | `str` | No | N/A | Base URL of the web interface. |
### `navidrome_version`<a id="variable-navidrome_version"></a>
[*⇑ Back to ToC ⇑*](#toc)
Version of the debian package.
- **Type**: `str`
- **Required**: No
- **Default**: `"0.54.5"`
### `navidrome_arch`<a id="variable-navidrome_arch"></a>
[*⇑ Back to ToC ⇑*](#toc)
Architecture of the debian package.
- **Type**: `str`
- **Required**: No
- **Default**: `"amd64"`
### `navidrome_user`<a id="variable-navidrome_user"></a>
[*⇑ Back to ToC ⇑*](#toc)
Operating system user to run the service.
- **Type**: `str`
- **Required**: No
- **Default**: `"navidrome"`
### `navidrome_group`<a id="variable-navidrome_group"></a>
[*⇑ Back to ToC ⇑*](#toc)
Operating system group to run the service.
- **Type**: `str`
- **Required**: No
- **Default**: `"navidrome"`
### `navidrome_music_folder`<a id="variable-navidrome_music_folder"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the music folder.
- **Type**: `path`
- **Required**: No
- **Default**: `"/opt/navidrome/music"`
### `navidrome_data_folder`<a id="variable-navidrome_data_folder"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the data directory.
- **Type**: `path`
- **Required**: No
- **Default**: `"/var/lib/navidrome"`
### `navidrome_cache_folder`<a id="variable-navidrome_cache_folder"></a>
[*⇑ Back to ToC ⇑*](#toc)
Path to the cache folder.
- **Type**: `path`
- **Required**: No
- **Default**: `"{{ navidrome_data_folder }}/cache"`
### `navidrome_manage_iptables`<a id="variable-navidrome_manage_iptables"></a>
[*⇑ Back to ToC ⇑*](#toc)
Configure iptables rules
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
### `navidrome_allowed_sources`<a id="variable-navidrome_allowed_sources"></a>
[*⇑ Back to ToC ⇑*](#toc)
List of IP ranges to allow when `navidrome_manage_iptables` is enabled
- **Type**: `list`
- **Required**: No
### `navidrome_address`<a id="variable-navidrome_address"></a>
[*⇑ Back to ToC ⇑*](#toc)
Address to listen.
- **Type**: `str`
- **Required**: No
- **Default**: `"localhost"`
### `navidrome_port`<a id="variable-navidrome_port"></a>
[*⇑ Back to ToC ⇑*](#toc)
Port to listen
- **Type**: `int`
- **Required**: No
- **Default**: `4533`
### `navidrome_enable_insights_collector`<a id="variable-navidrome_enable_insights_collector"></a>
[*⇑ Back to ToC ⇑*](#toc)
Enable the insights collector.
- **Type**: `bool`
- **Required**: No
- **Default**: `false`
### `navidrome_base_url`<a id="variable-navidrome_base_url"></a>
[*⇑ Back to ToC ⇑*](#toc)
Base URL of the web interface.
- **Type**: `str`
- **Required**: No
<!-- ANSIBLE DOCSMITH MAIN END -->
## Donate
As we all love FOSS, you should consider sponsoring the
[Navidrome](https://github.com/navidrome/navidrome) project.

View file

@ -0,0 +1,84 @@
---
# Version of the debian package.
#
# - Type: str
# - Required: No
# - Default: 0.54.5
navidrome_version: 0.54.5
# Architecture of the debian package.
#
# - Type: str
# - Required: No
# - Default: amd64
navidrome_arch: amd64
# Operating system user to run the service.
#
# - Type: str
# - Required: No
# - Default: navidrome
navidrome_user: navidrome
# Operating system group to run the service.
#
# - Type: str
# - Required: No
# - Default: navidrome
navidrome_group: navidrome
# Path to the music folder.
#
# - Type: path
# - Required: No
# - Default: /opt/navidrome/music
navidrome_music_folder: /opt/navidrome/music
# Path to the data directory.
#
# - Type: path
# - Required: No
# - Default: /var/lib/navidrome
navidrome_data_folder: /var/lib/navidrome
# Path to the cache folder.
#
# - Type: path
# - Required: No
# - Default: {{ navidrome_data_folder }}/cache
navidrome_cache_folder: "{{ navidrome_data_folder }}/cache"
# Configure iptables rules
#
# - Type: bool
# - Required: No
# - Default: false
navidrome_manage_iptables: false
# List of IP ranges to allow when `navidrome_manage_iptables` is enabled
#
# - Type: list
# - Required: No
navidrome_allowed_sources: []
# Address to listen.
#
# - Type: str
# - Required: No
# - Default: localhost
navidrome_address: localhost
# Port to listen
#
# - Type: int
# - Required: No
# - Default: 4533
navidrome_port: 4533
# Enable the insights collector.
#
# - Type: bool
# - Required: No
# - Default: false
navidrome_enable_insights_collector: false

View file

@ -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

View file

@ -0,0 +1,78 @@
---
argument_specs:
main:
short_description: Install Navidrome.
description:
- Install [Navidrome](https://github.com/navidrome/navidrome).
author:
- jriou
options:
navidrome_version:
description:
- Version of the debian package.
default: 0.54.5
navidrome_arch:
description:
- Architecture of the debian package.
default: amd64
navidrome_user:
description:
- Operating system user to run the service.
default: navidrome
navidrome_group:
description:
- Operating system group to run the service.
default: navidrome
navidrome_music_folder:
description:
- Path to the music folder.
type: path
default: /opt/navidrome/music
navidrome_data_folder:
description:
- Path to the data directory.
type: path
default: /var/lib/navidrome
navidrome_cache_folder:
description:
- Path to the cache folder.
type: path
default: "{{ navidrome_data_folder }}/cache"
navidrome_manage_iptables:
description:
- Configure iptables rules
type: bool
default: false
navidrome_allowed_sources:
description:
- List of IP ranges to allow when `navidrome_manage_iptables` is enabled
type: list
navidrome_address:
description:
- Address to listen.
default: localhost
navidrome_port:
description:
- Port to listen
type: int
default: 4533
navidrome_enable_insights_collector:
description:
- Enable the insights collector.
type: bool
default: false
navidrome_base_url:
description:
- Base URL of the web interface.

View file

@ -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

View file

@ -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 %}

View file

@ -0,0 +1,5 @@
{{ ansible_managed | comment }}
[Service]
User={{ navidrome_user }}
WorkingDirectory={{ navidrome_data_folder }}
ReadWritePaths={{ navidrome_data_folder }}

2
test-requirements.txt Normal file
View file

@ -0,0 +1,2 @@
ansible-core
molecule

3
test-requirements.yml Normal file
View file

@ -0,0 +1,3 @@
---
collections:
- name: containers.podman