Farid Siddiqi 7ab87de90d
All checks were successful
Build and Deploy API Server / build (push) Successful in 52s
Build and Deploy API Server / deploy (push) Successful in 4s
update build-deploy
2025-12-25 09:49:14 -08:00

557 lines
18 KiB
YAML

---
- name: Install Gitea (Ubuntu 24.04 minimal)
hosts: git
become: true
vars:
gitea_version: "1.23.3"
gitea_arch: "linux-amd64"
gitea_bin: /usr/local/bin/gitea
gitea_user: git
gitea_home: /var/lib/gitea
gitea_http_addr: "0.0.0.0" # Listen on all interfaces
gitea_http_port: 3000
gitea_domain: "{{ ansible_host }}" # Use the server's IP address
gitea_root_url: "http://{{ ansible_host }}" # HTTP with IP and port
gitea_internal_token: "5982634986925861987263497269412869416237419264" # Change to a secure random token
postgres_db: gitea
postgres_user: gitea
postgres_password: "12409768234691236"
# PostgreSQL admin user for debugging and management
postgres_admin_user: pgadmin
postgres_admin_password: "{{ lookup('env', 'POSTGRES_ADMIN_PASSWORD') | default('ChangeMe_SecurePassword_2025', true) }}"
# Golang API Server database user
api_server_db_user: apiserver
api_server_db_password: "{{ lookup('env', 'API_SERVER_DB_PASSWORD') | default('ChangeMe_ApiServer_2025', true) }}"
# NEW: Option to create separate database for API server
create_separate_apiserver_db: true # Set to false to share gitea database
# Gitea Runner configuration
runner_version: "0.2.11"
runner_count: 5 # Number of runners to deploy
runner_base_dir: /var/lib/gitea-runner
runner_token: "CHANGE_ME_TO_REGISTRATION_TOKEN" # Get from Gitea admin panel
tasks:
- name: Install packages
ansible.builtin.apt:
update_cache: true
name:
- ca-certificates
- curl
- git
- nginx
- ufw
- postgresql
- postgresql-contrib
- python3-psycopg # Important on Ubuntu 24.04
- docker.io
state: present
- name: Start and enable Docker
ansible.builtin.service:
name: docker
state: started
enabled: true
- name: Create git system user
ansible.builtin.user:
name: "{{ gitea_user }}"
system: true
create_home: true
home: "{{ gitea_home }}"
shell: /usr/sbin/nologin
groups: docker
append: true
- name: Create Gitea directories
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ gitea_user }}"
group: "{{ gitea_user }}"
mode: "0750"
loop:
- /etc/gitea
- "{{ gitea_home }}"
- "{{ gitea_home }}/custom"
- "{{ gitea_home }}/data"
- "{{ gitea_home }}/log"
- "{{ gitea_home }}/repositories"
- "{{ gitea_home }}/data/actions_artifacts"
- "{{ gitea_home }}/data/actions_cache"
- name: Download gitea binary
ansible.builtin.get_url:
url: "https://dl.gitea.com/gitea/{{ gitea_version }}/gitea-{{ gitea_version }}-{{ gitea_arch }}"
dest: "{{ gitea_bin }}"
mode: "0755"
- name: Ensure postgres running
ansible.builtin.service:
name: postgresql
state: started
enabled: true
- name: Create postgres user
become_user: postgres
community.postgresql.postgresql_user:
name: "{{ postgres_user }}"
password: "{{ postgres_password }}"
- name: Create postgres database
become_user: postgres
community.postgresql.postgresql_db:
name: "{{ postgres_db }}"
owner: "{{ postgres_user }}"
- name: Create PostgreSQL admin user for production debugging
become_user: postgres
community.postgresql.postgresql_user:
name: "{{ postgres_admin_user }}"
password: "{{ postgres_admin_password }}"
role_attr_flags: SUPERUSER,CREATEDB,CREATEROLE,REPLICATION,LOGIN
comment: "Admin user for debugging and managing databases, users, and roles"
- name: Create PostgreSQL user for golang-api-server
become_user: postgres
community.postgresql.postgresql_user:
name: "{{ api_server_db_user }}"
password: "{{ api_server_db_password }}"
role_attr_flags: LOGIN
comment: "Database user for golang-api-server application"
- name: Create separate API server database
become_user: postgres
community.postgresql.postgresql_db:
name: apiserver
owner: "{{ api_server_db_user }}"
when: create_separate_apiserver_db
- name: Grant privileges to api-server user on their database
become_user: postgres
community.postgresql.postgresql_privs:
database: "{{ 'apiserver' if create_separate_apiserver_db else postgres_db }}"
state: present
privs: ALL
type: database
role: "{{ api_server_db_user }}"
- name: Configure PostgreSQL to listen on all interfaces
ansible.builtin.lineinfile:
path: /etc/postgresql/16/main/postgresql.conf
regexp: '^#?listen_addresses\s*='
line: "listen_addresses = '*'"
backup: yes
notify: restart postgresql
- name: Allow remote connections to PostgreSQL
ansible.builtin.lineinfile:
path: /etc/postgresql/16/main/pg_hba.conf
line: "host all all 0.0.0.0/0 scram-sha-256"
insertafter: EOF
backup: yes
notify: restart postgresql
- name: Write Gitea config
ansible.builtin.copy:
dest: /etc/gitea/app.ini
owner: "{{ gitea_user }}"
group: "{{ gitea_user }}"
mode: "0640"
content: |
APP_NAME = Gitea
RUN_USER = {{ gitea_user }}
RUN_MODE = prod
WORK_PATH = {{ gitea_home }}
[server]
DOMAIN = {{ gitea_domain }}
ROOT_URL = {{ gitea_root_url }}
HTTP_ADDR = {{ gitea_http_addr }}
HTTP_PORT = {{ gitea_http_port }}
START_SSH_SERVER = false
DISABLE_SSH = false
APP_DATA_PATH = {{ gitea_home }}/data
[database]
DB_TYPE = postgres
HOST = 127.0.0.1:5432
NAME = {{ postgres_db }}
USER = {{ postgres_user }}
PASSWD = {{ postgres_password }}
SSL_MODE = disable
[security]
INSTALL_LOCK = true
INTERNAL_TOKEN = {{ gitea_internal_token}}
[actions]
ENABLED = true
[actions.artifacts]
STORAGE_TYPE = local
MINIO_BASE_PATH = actions_artifacts/
[actions.artifacts.local]
PATH = {{ gitea_home }}/data/actions_artifacts
[log]
MODE = file
LEVEL = info
ROOT_PATH = {{ gitea_home }}/log
- name: Install systemd unit
ansible.builtin.copy:
dest: /etc/systemd/system/gitea.service
mode: "0644"
content: |
[Unit]
Description=Gitea
After=network.target postgresql.service
Wants=postgresql.service
[Service]
Type=simple
User={{ gitea_user }}
Group={{ gitea_user }}
WorkingDirectory={{ gitea_home }}
Environment=HOME={{ gitea_home }}
ExecStart={{ gitea_bin }} web --config /etc/gitea/app.ini
Restart=always
RestartSec=2s
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.target
- name: Start gitea
ansible.builtin.systemd:
daemon_reload: true
name: gitea
state: started
enabled: true
# Gitea Runner Installation
- name: Delete all runners from Gitea database directly
become_user: postgres
community.postgresql.postgresql_query:
db: "{{ postgres_db }}"
query: "DELETE FROM action_runner;"
ignore_errors: true
- name: Stop all existing runner services
ansible.builtin.systemd:
name: "gitea-runner-{{ item }}"
state: stopped
enabled: false
loop: "{{ range(1, 21) | list }}" # Check up to 20 possible runners
ignore_errors: true
failed_when: false
- name: Remove all existing runner systemd units
ansible.builtin.file:
path: "/etc/systemd/system/gitea-runner-{{ item }}.service"
state: absent
loop: "{{ range(1, 21) | list }}"
ignore_errors: true
- name: Reload systemd after removing runner units
ansible.builtin.systemd:
daemon_reload: true
- name: Remove all existing runner directories
ansible.builtin.file:
path: "{{ runner_base_dir }}"
state: absent
- name: Recreate base runner directory
ansible.builtin.file:
path: "{{ runner_base_dir }}"
state: directory
owner: "{{ gitea_user }}"
group: "{{ gitea_user }}"
mode: "0750"
- name: Download Gitea runner binary
ansible.builtin.get_url:
url: "https://dl.gitea.com/act_runner/{{ runner_version }}/act_runner-{{ runner_version }}-linux-amd64"
dest: /usr/local/bin/act_runner
mode: "0755"
- name: Create runner directories
ansible.builtin.file:
path: "{{ runner_base_dir }}/runner-{{ item }}"
state: directory
owner: "{{ gitea_user }}"
group: "{{ gitea_user }}"
mode: "0750"
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Generate runner config
ansible.builtin.shell: |
/usr/local/bin/act_runner generate-config > {{ runner_base_dir }}/runner-{{ item }}/config.yaml
args:
creates: "{{ runner_base_dir }}/runner-{{ item }}/config.yaml"
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Configure runner container volumes
ansible.builtin.lineinfile:
path: "{{ runner_base_dir }}/runner-{{ item }}/config.yaml"
insertafter: "^container:"
line: " options: -v /opt/api-artifacts:/opt/api-artifacts -v /etc/systemd/system:/etc/systemd/system:ro"
regexp: "^ options:"
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Allow all volume mounts in runner config
ansible.builtin.replace:
path: "{{ runner_base_dir }}/runner-{{ item }}/config.yaml"
regexp: "^ valid_volumes: \\[\\]$"
replace: " valid_volumes:\n - '**'"
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Set runner config ownership
ansible.builtin.file:
path: "{{ runner_base_dir }}/runner-{{ item }}/config.yaml"
owner: "{{ gitea_user }}"
group: "{{ gitea_user }}"
mode: "0640"
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Generate runner registration tokens
ansible.builtin.shell: |
sudo -u {{ gitea_user }} {{ gitea_bin }} --config /etc/gitea/app.ini actions generate-runner-token
register: runner_tokens
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Register runners with generated tokens
become: true
become_user: "{{ gitea_user }}"
ansible.builtin.shell: |
cd {{ runner_base_dir }}/runner-{{ item.item }} && \
/usr/local/bin/act_runner register \
--instance {{ gitea_root_url }} \
--token {{ item.stdout | trim }} \
--config {{ runner_base_dir }}/runner-{{ item.item }}/config.yaml \
--name runner-{{ item.item }} \
--no-interactive
loop: "{{ runner_tokens.results }}"
args:
creates: "{{ runner_base_dir }}/runner-{{ item.item }}/.runner"
- name: Create runner systemd units
ansible.builtin.copy:
dest: "/etc/systemd/system/gitea-runner-{{ item }}.service"
mode: "0644"
content: |
[Unit]
Description=Gitea Runner {{ item }}
After=network.target gitea.service
Wants=gitea.service
[Service]
Type=simple
User={{ gitea_user }}
Group={{ gitea_user }}
WorkingDirectory={{ runner_base_dir }}/runner-{{ item }}
ExecStart=/usr/local/bin/act_runner daemon --config {{ runner_base_dir }}/runner-{{ item }}/config.yaml
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Enable and start runner services
ansible.builtin.systemd:
daemon_reload: true
name: "gitea-runner-{{ item }}"
enabled: true
state: started
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Restart runners to apply volume mount changes
ansible.builtin.systemd:
name: "gitea-runner-{{ item }}"
state: restarted
loop: "{{ range(1, runner_count + 1) | list }}"
- name: Configure nginx reverse proxy (HTTP only for now)
ansible.builtin.copy:
dest: /etc/nginx/sites-available/gitea
mode: "0644"
content: |
server {
listen 80;
server_name {{ ansible_host }};
# Allow large artifact uploads
client_max_body_size 500M;
location / {
proxy_pass http://127.0.0.1:{{ gitea_http_port }};
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- name: Enable nginx site
ansible.builtin.file:
src: /etc/nginx/sites-available/gitea
dest: /etc/nginx/sites-enabled/gitea
state: link
force: true
- name: Disable default nginx site
ansible.builtin.file:
path: /etc/nginx/sites-enabled/default
state: absent
- name: Test and reload nginx
ansible.builtin.shell: |
nginx -t && systemctl reload nginx
args:
executable: /bin/bash
- name: Allow firewall (SSH + HTTP)
ansible.builtin.ufw:
rule: allow
name: "{{ item }}"
loop:
- "OpenSSH"
- "Nginx Full"
- name: Allow PostgreSQL port
ansible.builtin.ufw:
rule: allow
port: 5432
- name: Enable UFW
ansible.builtin.ufw:
state: enabled
policy: deny
# ==========================================
# API SERVER DEPLOYMENT INFRASTRUCTURE
# ==========================================
- name: Create apiserver system user
ansible.builtin.user:
name: apiserver
system: true
create_home: true
home: /opt/api-artifacts
shell: /bin/bash
groups: docker
append: true
- name: Create deployment directory structure
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: apiserver
group: apiserver
mode: "0755"
loop:
- /opt/api-artifacts
- /opt/api-artifacts/builds
- /opt/api-artifacts/scripts
- /opt/api-artifacts/config
- name: Copy deployment scripts from local repository
ansible.builtin.copy:
src: "{{ playbook_dir }}/../scripts/{{ item }}"
dest: /opt/api-artifacts/scripts/{{ item }}
owner: apiserver
group: apiserver
mode: "0755"
loop:
- deploy.sh
- promote.sh
- rollback.sh
- health-check.sh
- migrate.sh
- name: Create production environment file
ansible.builtin.copy:
dest: /opt/api-artifacts/config/.env.production
owner: apiserver
group: apiserver
mode: "0600"
content: |
DATABASE_URL=postgresql://{{ api_server_db_user }}:{{ api_server_db_password }}@localhost:5432/{{ 'apiserver' if create_separate_apiserver_db else postgres_db }}?sslmode=disable
AUTO_MIGRATE=false
PORT=8080
GIN_MODE=release
- name: Copy systemd service file
ansible.builtin.copy:
src: "{{ playbook_dir }}/../systemd/api-server.service"
dest: /etc/systemd/system/api-server.service
owner: root
group: root
mode: "0644"
register: systemd_service
- name: Reload systemd daemon
ansible.builtin.systemd:
daemon_reload: true
when: systemd_service.changed
- name: Enable api-server service (but don't start - no binary yet)
ansible.builtin.systemd:
name: api-server.service
enabled: true
failed_when: false
- name: Install golang-migrate CLI
ansible.builtin.shell: |
curl -L https://github.com/golang-migrate/migrate/releases/download/v4.19.1/migrate.linux-amd64.tar.gz | tar xvz
mv migrate /usr/local/bin/
chmod +x /usr/local/bin/migrate
args:
creates: /usr/local/bin/migrate
- name: Display setup summary
ansible.builtin.debug:
msg:
- "============================================"
- "Gitea + API Server Infrastructure Complete!"
- "============================================"
- ""
- "Gitea:"
- " URL: {{ gitea_root_url }}"
- " Runners: {{ runner_count }} runners active"
- ""
- "API Server Deployment:"
- " Artifact directory: /opt/api-artifacts/"
- " Database: {{ 'apiserver' if create_separate_apiserver_db else postgres_db }}"
- " Database user: {{ api_server_db_user }}"
- " Systemd service: api-server.service (enabled)"
- ""
- "Next Steps:"
- "1. Create repository in Gitea and push your golang-api-server code"
- "2. Push will trigger Gitea Actions build automatically"
- "3. After build completes, SSH to server and promote:"
- " sudo /opt/api-artifacts/scripts/promote.sh"
- ""
- "Management Commands:"
- " - View builds: ls /opt/api-artifacts/builds/"
- " - Check logs: journalctl -u api-server -f"
- " - Rollback: sudo /opt/api-artifacts/scripts/rollback.sh"
handlers:
- name: restart postgresql
ansible.builtin.service:
name: postgresql
state: restarted