From 3a5b5680cfa0467c61ec01d440cf26c935da367d Mon Sep 17 00:00:00 2001 From: Marty Oehme Date: Wed, 8 Dec 2021 09:16:32 +0100 Subject: [PATCH] Add gitea role Added base gitea docker setup role. Adds automatic unattended setup with default admin account and optional email notification setup. --- roles/gitea/README.md | 41 ++++++++++ roles/gitea/defaults/main.yml | 24 ++++++ roles/gitea/files/gitea | 2 + roles/gitea/handlers/main.yml | 62 +++++++++++++++ roles/gitea/meta/main.yml | 16 ++++ roles/gitea/tasks/main.yml | 95 +++++++++++++++++++++++ roles/gitea/templates/docker-stack.yml.j2 | 68 ++++++++++++++++ roles/gitea/templates/upstream.json.j2 | 38 +++++++++ roles/gitea/vars/main.yml | 9 +++ site.yml | 5 ++ 10 files changed, 360 insertions(+) create mode 100644 roles/gitea/README.md create mode 100644 roles/gitea/defaults/main.yml create mode 100755 roles/gitea/files/gitea create mode 100644 roles/gitea/handlers/main.yml create mode 100644 roles/gitea/meta/main.yml create mode 100644 roles/gitea/tasks/main.yml create mode 100644 roles/gitea/templates/docker-stack.yml.j2 create mode 100644 roles/gitea/templates/upstream.json.j2 create mode 100644 roles/gitea/vars/main.yml diff --git a/roles/gitea/README.md b/roles/gitea/README.md new file mode 100644 index 0000000..60a0b99 --- /dev/null +++ b/roles/gitea/README.md @@ -0,0 +1,41 @@ +# gitea + +A relatively light-weight git server hosting. + +## Defaults + +``` +gitea_upstream_file_dir: "{{ docker_stack_files_dir }}/{{ stack_name }}" +``` + +The on-target directory where the proxy configuration file should be stashed. + +``` +gitea_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`. + +``` +gitea_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 `gitea.yourdomain.com` instead. + +For now gitea will still need to be initially set up after installation. +This could be automated with the help of these commands: + +```sh +docker run --name gitea -p 8080:3000 -e GITEA__security__INSTALL_LOCK=true -d gitea/gitea:1.14.2 + +$ docker exec gitea migrate +$ docker exec gitea gitea admin user create --admin --username root --password admin1234 --email admin@example.com +``` diff --git a/roles/gitea/defaults/main.yml b/roles/gitea/defaults/main.yml new file mode 100644 index 0000000..136085a --- /dev/null +++ b/roles/gitea/defaults/main.yml @@ -0,0 +1,24 @@ +--- + +# never got around to removing the master tag from the images +gitea_version: latest + +gitea_upstream_file_dir: "{{ docker_stack_files_dir }}/{{ stack_name }}" + +gitea_use_https: true + +# the subdomain link gitea will be reachable under +subdomain_alias: git + +gitea_db_database: gitea +gitea_db_username: gitea +gitea_db_password: gitea + +gitea_app_admin_username: Mygiteausername # can not be set to admin in Gitea +gitea_app_admin_password: Mygiteapassword +gitea_app_admin_email: myadmin@mydomain.mytld + +# gitea_smtp_host: domain.com:port +# gitea_smtp_username: my@username.com +# gitea_smtp_password: +# gitea_smtp_force_tls: false # forces tls if it is on a non-traditional tls port. Overwrites starttls so should generally be off diff --git a/roles/gitea/files/gitea b/roles/gitea/files/gitea new file mode 100755 index 0000000..c13d561 --- /dev/null +++ b/roles/gitea/files/gitea @@ -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 $@" diff --git a/roles/gitea/handlers/main.yml b/roles/gitea/handlers/main.yml new file mode 100644 index 0000000..68ebab7 --- /dev/null +++ b/roles/gitea/handlers/main.yml @@ -0,0 +1,62 @@ +- name: Add admin user + community.docker.docker_container_exec: + container: "{{ gitea_app_container_name['stdout'] }}" + command: > + gitea admin user create --admin --username {{ gitea_app_admin_username }} --password {{ gitea_app_admin_password }} --email {{ gitea_app_admin_email }} + become: yes + listen: "no admin user" + +## Register reverse proxy +- name: Ensure upstream directory exists + ansible.builtin.file: + path: "{{ gitea_upstream_file_dir }}" + state: directory + mode: '0755' + become: yes + listen: "update gitea upstream" + +- name: Update upstream template + ansible.builtin.template: + src: upstream.json.j2 + dest: "{{ gitea_upstream_file_dir }}/upstream.json" + mode: '0600' + become: yes + listen: "update gitea 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: yes + listen: "update gitea 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: yes + when: (result.stdout | from_json)['error'] is not defined + listen: "update gitea 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 @{{ gitea_upstream_file_dir }}/upstream.json localhost:2019/config/apps/http/servers/{{ (gitea_use_https == True) | ternary(caddy_https_server_name, caddy_http_server_name) }}/routes/0/ + become: yes + listen: "update gitea upstream" + +- name: Ensure upstream directory is gone again + ansible.builtin.file: + path: "{{ gitea_upstream_file_dir }}" + state: absent + become: yes + listen: "update gitea upstream" + diff --git a/roles/gitea/meta/main.yml b/roles/gitea/meta/main.yml new file mode 100644 index 0000000..da07f4a --- /dev/null +++ b/roles/gitea/meta/main.yml @@ -0,0 +1,16 @@ +--- + +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 + - caddy diff --git a/roles/gitea/tasks/main.yml b/roles/gitea/tasks/main.yml new file mode 100644 index 0000000..3529038 --- /dev/null +++ b/roles/gitea/tasks/main.yml @@ -0,0 +1,95 @@ +--- +- name: Ensure git user exists with ssh key + ansible.builtin.user: + name: "{{ gitea_git_username }}" + generate_ssh_key: yes + ssh_key_type: rsa + ssh_key_bits: 4096 + ssh_key_comment: "Gitea Host Key" + become: yes + register: git_user + +- name: Ensure git passthrough command directory exists + ansible.builtin.file: + path: "/app/gitea/" + state: directory + mode: '0770' + owner: "{{ git_user['uid'] }}" + group: "{{ git_user['group'] }}" + become: yes + +- name: Save git passthrough command in right location + ansible.builtin.copy: + src: gitea + dest: "/app/gitea/gitea" + owner: "{{ git_user['uid'] }}" + group: "{{ git_user['group'] }}" + mode: '0750' + become: yes + +- name: Fetch keyfile + fetch: + src: "{{ git_user['home'] }}/.ssh/id_rsa.pub" + dest: "buffer/{{ansible_hostname}}-id_rsa.pub" + flat: yes + become: yes + +- 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: yes + +- name: Clean up buffer dir + ansible.builtin.file: + path: buffer + state: absent + delegate_to: localhost + +## install gitea 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: yes + notify: "update gitea upstream" + +- name: Deploy gitea to swarm + community.general.docker_stack: + name: "{{ stack_name }}" + state: present + prune: yes + compose: + - "{{ stack_compose }}" + become: yes + tags: + - docker-swarm + notify: "update gitea upstream" + +- name: Get app container info + ansible.builtin.command: + cmd: docker ps -q -f name={{ stack_name }}_app + become: yes + until: gitea_app_container_name['rc'] == 0 and gitea_app_container_name['stdout'] | length >= 1 + retries: 5 + delay: 10 + changed_when: False + register: gitea_app_container_name + +- name: Look for existing admin user + community.docker.docker_container_exec: + container: "{{ gitea_app_container_name['stdout'] }}" + command: > + gitea admin user list --admin + become: yes + until: "'connection refused' not in gitea_admin_list and 'Failed to run app' not in gitea_admin_list" + retries: 5 + delay: 10 + changed_when: gitea_admin_list['stdout_lines'] | length <= 1 + failed_when: gitea_admin_list['rc'] == 1 and gitea_admin_list['attempts'] >= 5 + register: gitea_admin_list + notify: "no admin user" diff --git a/roles/gitea/templates/docker-stack.yml.j2 b/roles/gitea/templates/docker-stack.yml.j2 new file mode 100644 index 0000000..aa1cb81 --- /dev/null +++ b/roles/gitea/templates/docker-stack.yml.j2 @@ -0,0 +1,68 @@ +version: '3.4' + +services: + app: + image: "{{ stack_image }}:{{ gitea_version }}" + healthcheck: + test: ["CMD", "wget", "--spider", "-q", "localhost: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'] }} + - GITEA__database__DB_TYPE=postgres + - GITEA__database__HOST=db:5432 + - GITEA__database__NAME={{ gitea_db_database }} + - GITEA__database__USER={{ gitea_db_username }} + - GITEA__database__PASSWD={{ gitea_db_password }} + - "GITEA__server__ROOT_URL={{ (gitea_use_https == True) | ternary('https', 'http') }}://{{ (subdomain_alias is not undefined and not none) | ternary(subdomain_alias, stack_name) }}.{{server_domain}}" + - "GITEA__server__SSH_DOMAIN={{ server_domain }}" + - GITEA__server__LANDINGPAGE=explore + - GITEA__service__DISABLE_REGISTRATION=true +{% if gitea_app_admin_username is not undefined and not None and gitea_app_admin_password is not undefined and not None %} + - GITEA__security__INSTALL_LOCK=true +{% endif %} +{% if gitea_smtp_host is not undefined and not None and gitea_smtp_username is not undefined and not None and gitea_smtp_password is not undefined and not None %} + - GITEA__mailer__ENABLED=true + - GITEA__service__ENABLE_NOTIFY_MAIL=true + - GITEA__mailer__FROM=gitea@{{ server_domain }} + - GITEA__mailer__TYPE=smtp + - GITEA__mailer__HOST={{ gitea_smtp_host }} + - GITEA__mailer__IS_TLS_ENABLED={{ (gitea_smtp_force_tls is not undefined and not None) | ternary(gitea_smtp_force_tls,'false') }} + - GITEA__mailer__USER={{ gitea_smtp_username }} + - GITEA__mailer__PASSWD={{ gitea_smtp_password }} +{% endif %} + networks: + - "{{ docker_swarm_public_network_name }}" + - backend + ports: + - "127.0.0.1:2222:22" + + db: + image: postgres:13 + volumes: + - db:/var/lib/postgresql/data + networks: + - backend + environment: + - POSTGRES_USER={{ gitea_db_username }} + - POSTGRES_PASSWORD={{ gitea_db_password }} + - POSTGRES_DB={{ gitea_db_database }} + +volumes: + data: + db: + +networks: + "{{ docker_swarm_public_network_name }}": + external: true + backend: + + diff --git a/roles/gitea/templates/upstream.json.j2 b/roles/gitea/templates/upstream.json.j2 new file mode 100644 index 0000000..e731ad4 --- /dev/null +++ b/roles/gitea/templates/upstream.json.j2 @@ -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" + } + ] + } + ] +} diff --git a/roles/gitea/vars/main.yml b/roles/gitea/vars/main.yml new file mode 100644 index 0000000..8fd0ae4 --- /dev/null +++ b/roles/gitea/vars/main.yml @@ -0,0 +1,9 @@ +--- + +stack_name: gitea + +stack_image: "gitea/gitea" + +stack_compose: "{{ lookup('template', 'docker-stack.yml.j2') | from_yaml }}" + +gitea_git_username: git diff --git a/site.yml b/site.yml index 0ec9a1f..5ed33b7 100644 --- a/site.yml +++ b/site.yml @@ -73,3 +73,8 @@ import_role: role: blog tags: blog + + - name: Install gitea + import_role: + role: gitea + tags: gitea