Switch site playbook to use forgejo

This commit is contained in:
Marty Oehme 2024-06-24 18:30:34 +02:00
parent 648f49a847
commit 9ec5b6dec6
Signed by: Marty
GPG key ID: EDBF2ED917B2EF6A
13 changed files with 4 additions and 4 deletions

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

@ -0,0 +1,40 @@
# forgejo
A relatively light-weight git server hosting.
## Defaults
```
forgejo_upstream_file_dir: "{{ docker_stack_files_dir }}/{{ stack_name }}"
```
The on-target directory where the proxy configuration file should be stashed.
```
forgejo_use_https: true
```
Whether the service should be reachable through http (port 80) or through https (port 443) and provision an https certificate. Usually you will want this to stay `true`.
```
forgejo_version: latest
```
The docker image version to be used in stack creation.
```
subdomain_alias: git
```
If the deployed container should be served over a uri that is not the stack name.
By default, it will be set to `git.yourdomain.com` -
if this option is not set it will be served on `forgejo.yourdomain.com` instead.
For now forgejo will still need to be initially set up after installation.
This could be automated with the help of these commands:
```sh
docker run --name forgejo -p 8080:3000 -e FORGEJO__security__INSTALL_LOCK=true -d codeberg.org/forgejo/forgejo:7
$ docker exec forgejo migrate
$ docker exec forgejo forgejo admin user create --admin --username root --password admin1234 --email admin@example.com
```

View file

@ -0,0 +1,32 @@
---
forgejo_version: 7
forgejo_upstream_file_dir: "{{ docker_stack_files_dir }}/{{ stack_name }}"
forgejo_use_https: true
# the subdomain link forgejo will be reachable under
subdomain_alias: git
subdomain_ci_alias: ci
forgejo_db_database: forgejo
forgejo_db_username: forgejo
forgejo_db_password: forgejo
forgejo_app_admin_username: Myforgejousername # can not be set to admin in Forgejo
forgejo_app_admin_password: Myforgejopassword
forgejo_app_admin_email: myadmin@mydomain.mytld
# forgejo_smtp_host: domain.com:port
# forgejo_smtp_username: my@username.com
# forgejo_smtp_password: <password>
# forgejo_smtp_force_tls: false # forces tls if it is on a non-traditional tls port. Overwrites starttls so should generally be off
forgejo_use_ci: false
# forgejo_ci_github_client:
# forgejo_ci_github_secret:
# forgejo_ci_gitlab_client:
# forgejo_ci_gitlab_secret:
# forgejo_ci_forgejo_client:
# forgejo_ci_forgejo_secret:

2
roles/forgejo/files/forgejo Executable file
View file

@ -0,0 +1,2 @@
#!/usr/bin/env sh
ssh -p 2222 -o StrictHostKeyChecking=no git@127.0.0.1 "SSH_ORIGINAL_COMMAND=\"$SSH_ORIGINAL_COMMAND\" $0 $@"

View file

@ -0,0 +1,100 @@
- name: Add admin user
community.docker.docker_container_exec:
container: "{{ forgejo_app_container_name['stdout'] }}"
command: >
forgejo admin user create --admin --username {{ forgejo_app_admin_username }} --password {{ forgejo_app_admin_password }} --email {{ forgejo_app_admin_email }}
user: git
become: true
listen: "no admin user"
## Register reverse proxy
- name: Upstream directory exists
ansible.builtin.file:
path: "{{ forgejo_upstream_file_dir }}"
state: directory
mode: "0755"
become: true
listen: "update forgejo upstream"
- name: Update upstream template
ansible.builtin.template:
src: upstream.json.j2
dest: "{{ forgejo_upstream_file_dir }}/upstream.json"
mode: "0600"
become: true
listen: "update forgejo upstream"
- name: Update ci upstream template
ansible.builtin.template:
src: upstream_ci.json.j2
dest: "{{ forgejo_upstream_file_dir }}/upstream_ci.json"
mode: "0600"
become: true
listen: "update forgejo upstream"
# figure out if upstream id exists
- name: check {{ stack_name }} upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl localhost:2019/id/{{ stack_name }}_upstream/
changed_when: False
register: result
become: true
listen: "update forgejo upstream"
# upstream already exists, patch it
- name: remove old {{ stack_name }} upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl -X DELETE localhost:2019/id/{{ stack_name }}_upstream/
become: true
when: (result.stdout | from_json)['error'] is not defined
listen: "update forgejo upstream"
# upstream has to be created
- name: add {{ stack_name }} upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl -X POST -H "Content-Type: application/json" -d @{{ forgejo_upstream_file_dir }}/upstream.json localhost:2019/config/apps/http/servers/{{ (forgejo_use_https == True) | ternary(caddy_https_server_name, caddy_http_server_name) }}/routes/0/
become: true
listen: "update forgejo upstream"
# figure out if upstream id exists
- name: check {{ stack_name }}_ci upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl localhost:2019/id/{{ stack_name }}_ci_upstream/
changed_when: False
register: result
become: true
listen: "update forgejo upstream"
# upstream for ci already exists, patch it
- name: remove old {{ stack_name }}_ci upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl -X DELETE localhost:2019/id/{{ stack_name }}_ci_upstream/
become: true
when: (result.stdout | from_json)['error'] is not defined
listen: "update forgejo upstream"
# upstream for ci has to be created
- name: add {{ stack_name }}_ci upstream
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl -X POST -H "Content-Type: application/json" -d @{{ forgejo_upstream_file_dir }}/upstream_ci.json localhost:2019/config/apps/http/servers/{{ (forgejo_use_https == True) | ternary(caddy_https_server_name, caddy_http_server_name) }}/routes/0/
become: true
listen: "update forgejo upstream"
- name: Ensure upstream directory is gone again
ansible.builtin.file:
path: "{{ forgejo_upstream_file_dir }}"
state: absent
become: true
listen: "update forgejo upstream"

View file

@ -0,0 +1,15 @@
---
galaxy_info:
author: Marty Oehme
description: Light-weight git hosting
license: GPL-3.0-only
min_ansible_version: 2.9
galaxy_tags: []
platforms:
- name: GenericLinux
versions: all
dependencies:
- docker
- docker-swarm

View file

@ -0,0 +1,12 @@
---
## install requisites
- name: Ensure openssl installed
ansible.builtin.package:
name: "openssl"
state: present
become: true
tags:
- apt
- download
- packages

View file

@ -0,0 +1,132 @@
---
## Prepare woodpecker ci
- name: "Select tasks for {{ ansible_distribution }} {{ ansible_distribution_major_version }}"
include_tasks: "{{ distribution }}"
with_first_found:
- "{{ ansible_distribution }}-{{ ansible_distribution_major_version }}.yml"
- "{{ ansible_distribution }}.yml"
- "{{ ansible_os_family }}.yml"
loop_control:
loop_var: distribution
when: forgejo_use_ci == True
# TODO only generate when no existing (check with docker inspect?)
- name: Generate agent key
ansible.builtin.shell: openssl rand -hex 32
register: forgejo_woodpecker_agent_secret
when: forgejo_use_ci == True
- name: Set agent key
ansible.builtin.set_fact:
forgejo_woodpecker_agent_secret: "{{ forgejo_woodpecker_agent_secret.stdout }}"
when: forgejo_woodpecker_agent_secret.stdout is not undefined and not None
## Prepare forgejo
- name: Ensure git user exists with ssh key
ansible.builtin.user:
name: "{{ forgejo_git_username }}"
generate_ssh_key: yes
ssh_key_type: rsa
ssh_key_bits: 4096
ssh_key_comment: "Forgejo Host Key"
become: true
register: git_user
- name: Ensure git passthrough command directory exists
ansible.builtin.file:
path: "/app/forgejo/"
state: directory
mode: '0770'
owner: "{{ git_user['uid'] }}"
group: "{{ git_user['group'] }}"
become: true
- name: Passthrough git command is in right location
ansible.builtin.copy:
src: forgejo
dest: "/app/forgejo/forgejo"
owner: "{{ git_user['uid'] }}"
group: "{{ git_user['group'] }}"
mode: '0750'
become: true
- name: Host machine forgejo command points to passthrough command
ansible.builtin.file:
state: link
src: "/app/forgejo/forgejo"
dest: "/usr/local/bin/forgejo"
become: true
- name: Fetch keyfile
fetch:
src: "{{ git_user['home'] }}/.ssh/id_rsa.pub"
dest: "buffer/{{ansible_hostname}}-id_rsa.pub"
flat: yes
become: true
- name: Ensure git user has its own key authorized for access
ansible.posix.authorized_key:
user: "{{ git_user['name'] }}"
state: present
key: "{{ lookup('file', 'buffer/{{ ansible_hostname }}-id_rsa.pub') }}"
become: true
- name: Clean up buffer dir
ansible.builtin.file:
path: buffer
state: absent
delegate_to: localhost
## install forgejo container
- name: Check upstream status
community.docker.docker_container_exec:
container: "{{ caddy_container_id }}"
command: >
curl localhost:2019/id/{{ stack_name }}_upstream/
register: result
changed_when: (result.stdout | from_json) != (lookup('template', 'upstream.json.j2') | from_yaml)
become: true
notify: "update forgejo upstream"
- name: Deploy forgejo to swarm
community.general.docker_stack:
name: "{{ stack_name }}"
state: present
prune: yes
compose:
- "{{ stack_compose }}"
become: true
tags:
- docker-swarm
register: forgejo_deployment
notify: "update forgejo upstream"
- name: Wait a minute for forgejo to become healthy
wait_for:
timeout: 55
delegate_to: localhost
when: forgejo_deployment is changed
- name: Get app container info
ansible.builtin.command:
cmd: docker ps -q -f name={{ stack_name }}_app
become: true
until: forgejo_app_container_name['rc'] | default('') == 0 and forgejo_app_container_name['stdout'] | length >= 1
retries: 10
delay: 10
changed_when: False
register: forgejo_app_container_name
- name: Look for existing admin user
community.docker.docker_container_exec:
container: "{{ forgejo_app_container_name['stdout'] }}"
user: git
command: >
forgejo admin user list --admin
until: forgejo_admin_list is defined and forgejo_admin_list['rc'] | default('') == 0
retries: 15
delay: 20
become: true
register: forgejo_admin_list
changed_when: forgejo_admin_list['stdout_lines'] | length <= 1 and 'Username' in forgejo_admin_list['stdout']
notify: "no admin user"

View file

@ -0,0 +1,116 @@
version: '3.4'
services:
app:
image: "{{ stack_image }}:{{ forgejo_version }}"
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "127.0.0.1:3000"]
interval: 1m
timeout: 10s
retries: 3
start_period: 1m
volumes:
- data:/data
- /home/git/.ssh:/data/git/.ssh
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID={{ git_user['uid'] }}
- USER_GID={{ git_user['group'] }}
- 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 }}"
- "FORGEJO__server__ROOT_URL={{ (forgejo_use_https == True) | ternary('https', 'http') }}://{{ (subdomain_alias is not undefined and not none) | ternary(subdomain_alias, stack_name) }}.{{server_domain}}"
- "FORGEJO__server__SSH_DOMAIN={{ (subdomain_alias is not undefined and not none) | ternary(subdomain_alias, stack_name) }}.{{server_domain}}"
- FORGEJO__server__LANDINGPAGE=explore
- FORGEJO__service__DISABLE_REGISTRATION=true
{% if forgejo_app_admin_username is not undefined and not None and forgejo_app_admin_password is not undefined and not None %}
- FORGEJO__security__INSTALL_LOCK=true
{% endif %}
{% if forgejo_smtp_host is not undefined and not None and forgejo_smtp_username is not undefined and not None and forgejo_smtp_password is not undefined and not None %}
- FORGEJO__mailer__ENABLED=true
- FORGEJO__service__ENABLE_NOTIFY_MAIL=true
- FORGEJO__mailer__FROM=forgejo@{{ server_domain }}
- FORGEJO__mailer__TYPE=smtp
- FORGEJO__mailer__HOST={{ forgejo_smtp_host }}
- FORGEJO__mailer__IS_TLS_ENABLED={{ (forgejo_smtp_force_tls is not undefined and not None) | ternary(forgejo_smtp_force_tls,'false') }}
- FORGEJO__mailer__USER={{ forgejo_smtp_username }}
- FORGEJO__mailer__PASSWD={{ forgejo_smtp_password }}
{% endif %}
networks:
- "{{ docker_swarm_public_network_name }}"
- backend
ports:
- "127.0.0.1:2222:22"
db:
image: postgres:13
healthcheck:
test: ["CMD", "pg_isready", "-q", "-U", "{{ forgejo_db_username }}"]
interval: 1m
timeout: 10s
retries: 3
start_period: 1m
volumes:
- db:/var/lib/postgresql/data
networks:
- backend
environment:
- POSTGRES_USER={{ forgejo_db_username }}
- POSTGRES_PASSWORD={{ forgejo_db_password }}
- POSTGRES_DB={{ forgejo_db_database }}
{% if forgejo_use_ci %}
wp-server:
image: woodpeckerci/woodpecker-server:latest
networks:
- "{{ docker_swarm_public_network_name }}"
- backend
volumes:
- woodpecker:/var/lib/woodpecker/
environment:
- WOODPECKER_OPEN=true
- "WOODPECKER_HOST={{ (forgejo_use_https == True) | ternary('https', 'http') }}://{{ (subdomain_ci_alias is not undefined and not none) | ternary(subdomain_ci_alias, stack_name + '_ci') }}.{{server_domain}}"
- WOODPECKER_AGENT_SECRET={{ forgejo_woodpecker_agent_secret }}
{% if forgejo_ci_github_client is not undefined and not None and forgejo_ci_github_secret is not undefined and not None %}
- WOODPECKER_GITHUB=true
- WOODPECKER_GITHUB_CLIENT={{ forgejo_ci_github_client }}
- WOODPECKER_GITHUB_SECRET={{ forgejo_ci_github_secret }}
{% endif %}
{% if forgejo_ci_gitlab_client is not undefined and not None and forgejo_ci_gitlab_secret is not undefined and not None %}
- WOODPECKER_GITLAB=true
- WOODPECKER_gitlab_CLIENT={{ forgejo_ci_gitlab_client }}
- WOODPECKER_gitlab_SECRET={{ forgejo_ci_gitlab_secret }}
{% endif %}
{% if forgejo_ci_forgejo_client is not undefined and not None and forgejo_ci_forgejo_secret is not undefined and not None %}
- WOODPECKER_FORGEJO=true
- "WOODPECKER_FORGEJO_URL={{ (forgejo_use_https == True) | ternary('https', 'http') }}://{{ (subdomain_alias is not undefined and not none) | ternary(subdomain_alias, stack_name) }}.{{server_domain}}"
- WOODPECKER_FORGEJO_CLIENT={{ forgejo_ci_forgejo_client }}
- WOODPECKER_FORGEJO_SECRET={{ forgejo_ci_forgejo_secret }}
{% endif %}
wp-agent:
image: woodpeckerci/woodpecker-agent:latest
networks:
- backend
command: agent
volumes:
- /var/run/docker.sock:/var/run/docker.sock
environment:
- WOODPECKER_SERVER=wp-server:9000
- WOODPECKER_AGENT_SECRET={{ forgejo_woodpecker_agent_secret }}
{% endif %}
volumes:
data:
db:
woodpecker:
networks:
"{{ docker_swarm_public_network_name }}":
external: true
backend:

View file

@ -0,0 +1,38 @@
{
"@id": "{{ stack_name }}_upstream",
{% if server_domain is not undefined and not none %}
"match": [
{
"host": [
{% if subdomain_alias is not undefined and not none %}
"{{ subdomain_alias }}.{{ server_domain }}"
{% else %}
"{{ stack_name }}.{{ server_domain }}"
{% endif %}
]
}
],
{% else %}
"match": [
{
"path": [
{% if subdomain_alias is not undefined and not none %}
"/{{ subdomain_alias }}*"
{% else %}
"/{{ stack_name }}*"
{% endif %}
]
}
],
{% endif %}
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "{{ stack_name }}_app:3000"
}
]
}
]
}

View file

@ -0,0 +1,39 @@
{
"@id": "{{ stack_name }}_ci_upstream",
{% if server_domain is not undefined and not none %}
"match": [
{
"host": [
{% if subdomain_ci_alias is not undefined and not none %}
"{{ subdomain_ci_alias }}.{{ server_domain }}"
{% else %}
"{{ stack_name }}_ci.{{ server_domain }}"
{% endif %}
]
}
],
{% else %}
"match": [
{
"path": [
{% if subdomain_ci_alias is not undefined and not none %}
"/{{ subdomain_ci_alias }}*"
{% else %}
"/{{ stack_name }}_ci*"
{% endif %}
]
}
],
{% endif %}
"handle": [
{
"handler": "reverse_proxy",
"upstreams": [
{
"dial": "{{ stack_name }}_wp-server:8000"
}
]
}
]
}

View file

@ -0,0 +1,9 @@
---
stack_name: forgejo
stack_image: "codeberg.org/forgejo/forgejo"
stack_compose: "{{ lookup('template', 'docker-stack.yml.j2') | from_yaml }}"
forgejo_git_username: git