feat: add Gitvana deployment role with configuration and service management

This commit is contained in:
nathan 2026-04-19 19:44:31 -04:00
parent 129b7eee1b
commit 9286cdb331
15 changed files with 458 additions and 0 deletions

View File

@ -0,0 +1,11 @@
---
# Gitvana deployment defaults. Override these in host_vars or via -e as needed.
gitvana_repo_url: https://git.castaldifamily.com/nathan/gitvana
gitvana_repo_version: main
# Runtime mode can be dev or preview. Preview is recommended for long-running service.
gitvana_run_mode: preview
gitvana_service_port: 3000
# Optional: If repository access requires a dedicated SSH key on target hosts, set this path.
# gitvana_git_key_file: /home/chester/.ssh/id_ed25519

View File

@ -0,0 +1,87 @@
# Run Gitvana Bun Deployment
This runbook deploys Gitvana directly to Linux VM/LXC hosts using the Ansible role `gitvana_bun_host`.
## Files added
- `roles/gitvana_bun_host/*`
- `playbooks/deploy-gitvana-bun.yml`
- `group_vars/all/gitvana_bun.yml`
## 1) Prepare control node
From the repository root:
```bash
cd ansible
ansible --version
ansible-galaxy collection install -r requirements.yml
```
## 2) Confirm target host access
Use your existing inventory and test connectivity:
```bash
ansible -i inventory/hosts.ini docker_nodes -m ping
```
If you want a single host, replace `docker_nodes` with a host like `heimdall`.
## 3) Review or override deployment variables
Default values are in `group_vars/all/gitvana_bun.yml` and role defaults.
Common overrides:
```bash
-e gitvana_target_hosts=heimdall
-e gitvana_repo_version=main
-e gitvana_service_port=3000
-e gitvana_run_mode=preview
```
## 4) Run deployment
Deploy to all `docker_nodes`:
```bash
ansible-playbook -i inventory/hosts.ini playbooks/deploy-gitvana-bun.yml
```
Deploy to one host:
```bash
ansible-playbook -i inventory/hosts.ini playbooks/deploy-gitvana-bun.yml -e gitvana_target_hosts=heimdall
```
## 5) Verify service
Check status on target host:
```bash
sudo systemctl status gitvana --no-pager
sudo journalctl -u gitvana -n 100 --no-pager
curl -I http://127.0.0.1:3000/
```
## 6) Day-2 operations
Redeploy after code updates:
```bash
ansible-playbook -i inventory/hosts.ini playbooks/deploy-gitvana-bun.yml -e gitvana_target_hosts=heimdall
```
Restart service only:
```bash
ansible -i inventory/hosts.ini heimdall -b -m ansible.builtin.systemd -a "name=gitvana state=restarted"
```
## Troubleshooting quick checks
- Ensure Bun is present: `which bun && bun --version`
- Ensure app directory is owned by runtime user: `ls -la /opt/gitvana`
- Ensure service unit exists: `cat /etc/systemd/system/gitvana.service`
- Ensure selected host can access the git repository URL over network

View File

@ -0,0 +1,15 @@
---
- name: Deploy Gitvana on Linux VM/LXC hosts with Bun
hosts: "{{ gitvana_target_hosts | default('docker_nodes') }}"
gather_facts: true
become: true
pre_tasks:
- name: Validate target host pattern
ansible.builtin.assert:
that:
- (gitvana_target_hosts | default('docker_nodes')) | length > 0
fail_msg: "gitvana_target_hosts must not be empty."
roles:
- role: gitvana_bun_host

View File

@ -0,0 +1,40 @@
# gitvana_bun_host
Ansible role to deploy Gitvana directly on a Linux VM/LXC host using Bun and systemd.
## What this role does
- Installs required OS packages
- Installs Bun from official GitHub releases
- Clones/updates the Gitvana repository
- Installs dependencies with Bun
- Creates and manages a systemd unit
- Verifies service reachability over HTTP
## Requirements
- Target OS: Debian 12+ or Ubuntu 22.04+
- Ansible Core 2.16+
- SSH access with privilege escalation rights
## Role variables
See defaults in defaults/main.yml.
Most commonly overridden:
- gitvana_repo_url
- gitvana_repo_version
- gitvana_service_port
- gitvana_run_mode
## Example playbook
```yaml
---
- name: Deploy Gitvana with Bun
hosts: docker_nodes
become: true
roles:
- role: gitvana_bun_host
```

View File

@ -0,0 +1,30 @@
---
gitvana_repo_url: https://git.castaldifamily.com/nathan/gitvana
gitvana_repo_version: main
gitvana_app_user: gitvana
gitvana_app_group: gitvana
gitvana_app_root: /opt/gitvana
gitvana_service_name: gitvana
gitvana_service_port: 3000
gitvana_run_mode: preview
gitvana_env:
NODE_ENV: production
PORT: "{{ gitvana_service_port }}"
gitvana_healthcheck_path: /
gitvana_verify_status_codes:
- 200
- 301
- 302
bun_version: 1.2.15
bun_install_root: /opt/bun
gitvana_bun_arch_map:
x86_64: linux-x64-baseline
aarch64: linux-aarch64
# Optional SSH deploy key for private repositories.
gitvana_git_key_file: ""

View File

@ -0,0 +1,9 @@
---
- name: Reload systemd
ansible.builtin.systemd:
daemon_reload: true
- name: Restart Gitvana service
ansible.builtin.systemd:
name: "{{ gitvana_service_name }}"
state: restarted

View File

@ -0,0 +1,26 @@
---
galaxy_info:
role_name: gitvana_bun_host
namespace: homelab
author: FrankGPT
description: Deploy Gitvana directly on Debian/Ubuntu hosts using Bun and systemd
license: MIT
min_ansible_version: "2.16"
platforms:
- name: Ubuntu
versions:
- jammy
- noble
- name: Debian
versions:
- bookworm
galaxy_tags:
- bun
- web
- application
- deployment
- lxc
dependencies: []

View File

@ -0,0 +1,35 @@
---
- name: Clone or update Gitvana repository
ansible.builtin.git:
repo: "{{ gitvana_repo_url }}"
dest: "{{ gitvana_app_root }}"
version: "{{ gitvana_repo_version }}"
update: true
key_file: "{{ gitvana_git_key_file | default(omit) }}"
accept_hostkey: true
become: true
become_user: "{{ gitvana_app_user }}"
register: gitvana_repo_sync
- name: Ensure repository ownership is correct
ansible.builtin.file:
path: "{{ gitvana_app_root }}"
state: directory
recurse: true
owner: "{{ gitvana_app_user }}"
group: "{{ gitvana_app_group }}"
- name: Check if dependencies directory exists
ansible.builtin.stat:
path: "{{ gitvana_app_root }}/node_modules"
register: gitvana_node_modules
- name: Install application dependencies with Bun
ansible.builtin.command:
cmd: /usr/local/bin/bun install --frozen-lockfile
chdir: "{{ gitvana_app_root }}"
become: true
become_user: "{{ gitvana_app_user }}"
when: gitvana_repo_sync.changed or not gitvana_node_modules.stat.exists
changed_when: true
notify: Restart Gitvana service

View File

@ -0,0 +1,71 @@
---
- name: Ensure Bun install root exists
ansible.builtin.file:
path: "{{ bun_install_root }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Set Bun artifact values
ansible.builtin.set_fact:
bun_archive_name: "bun-{{ bun_arch_suffix }}.zip"
bun_download_url: "https://github.com/oven-sh/bun/releases/download/bun-v{{ bun_version }}/bun-{{ bun_arch_suffix }}.zip"
bun_archive_path: "/tmp/bun-v{{ bun_version }}-{{ bun_arch_suffix }}.zip"
bun_extract_path: "{{ bun_install_root }}/bun-v{{ bun_version }}-{{ bun_arch_suffix }}"
- name: Ensure versioned Bun extract directory exists
ansible.builtin.file:
path: "{{ bun_extract_path }}"
state: directory
owner: root
group: root
mode: "0755"
- name: Download Bun release archive
ansible.builtin.get_url:
url: "{{ bun_download_url }}"
dest: "{{ bun_archive_path }}"
mode: "0644"
- name: Extract Bun archive
ansible.builtin.unarchive:
src: "{{ bun_archive_path }}"
dest: "{{ bun_extract_path }}"
remote_src: true
creates: "{{ bun_extract_path }}/.extracted"
- name: Mark Bun extraction complete
ansible.builtin.file:
path: "{{ bun_extract_path }}/.extracted"
state: touch
mode: "0644"
- name: Locate extracted Bun binary
ansible.builtin.find:
paths: "{{ bun_extract_path }}"
recurse: true
file_type: file
patterns: bun
register: bun_binary_find
- name: Fail when Bun binary is missing
ansible.builtin.fail:
msg: "Bun binary was not found under {{ bun_extract_path }} after extraction."
when: bun_binary_find.matched | int == 0
- name: Select Bun binary path
ansible.builtin.set_fact:
bun_binary_path: "{{ (bun_binary_find.files | sort(attribute='path') | first).path }}"
- name: Ensure Bun binary has execute permission
ansible.builtin.file:
path: "{{ bun_binary_path }}"
mode: "0755"
- name: Link Bun binary into standard PATH
ansible.builtin.file:
src: "{{ bun_binary_path }}"
dest: /usr/local/bin/bun
state: link
force: true

View File

@ -0,0 +1,18 @@
---
- name: Validate role input
ansible.builtin.import_tasks: validate.yml
- name: Install base prerequisites
ansible.builtin.import_tasks: prereqs.yml
- name: Install Bun runtime
ansible.builtin.import_tasks: install_bun.yml
- name: Deploy application code
ansible.builtin.import_tasks: deploy_code.yml
- name: Configure and start service
ansible.builtin.import_tasks: service.yml
- name: Verify service reachability
ansible.builtin.import_tasks: verify.yml

View File

@ -0,0 +1,31 @@
---
- name: Install prerequisite packages
ansible.builtin.apt:
name:
- ca-certificates
- curl
- git
- unzip
state: present
update_cache: true
- name: Ensure application group exists
ansible.builtin.group:
name: "{{ gitvana_app_group }}"
system: true
- name: Ensure application user exists
ansible.builtin.user:
name: "{{ gitvana_app_user }}"
group: "{{ gitvana_app_group }}"
system: true
create_home: false
shell: /usr/sbin/nologin
- name: Ensure application directory exists
ansible.builtin.file:
path: "{{ gitvana_app_root }}"
state: directory
owner: "{{ gitvana_app_user }}"
group: "{{ gitvana_app_group }}"
mode: "0755"

View File

@ -0,0 +1,17 @@
---
- name: Render systemd unit file
ansible.builtin.template:
src: gitvana.service.j2
dest: "/etc/systemd/system/{{ gitvana_service_name }}.service"
owner: root
group: root
mode: "0644"
notify:
- Reload systemd
- Restart Gitvana service
- name: Ensure Gitvana service is enabled and running
ansible.builtin.systemd:
name: "{{ gitvana_service_name }}"
enabled: true
state: started

View File

@ -0,0 +1,31 @@
---
- name: Assert supported operating system
ansible.builtin.assert:
that:
- ansible_os_family == "Debian"
fail_msg: "This role supports Debian/Ubuntu targets only."
- name: Assert required role variables
ansible.builtin.assert:
that:
- gitvana_repo_url | length > 0
- gitvana_repo_version | length > 0
- gitvana_app_user | length > 0
- gitvana_app_group | length > 0
- gitvana_app_root | length > 0
- gitvana_service_name | length > 0
- gitvana_service_port | int > 0
- gitvana_service_port | int < 65536
- bun_version | length > 0
- bun_install_root | length > 0
- gitvana_run_mode in ["dev", "preview"]
fail_msg: "One or more required variables are missing or invalid."
- name: Map architecture for Bun release artifact
ansible.builtin.set_fact:
bun_arch_suffix: "{{ gitvana_bun_arch_map.get(ansible_architecture) }}"
- name: Fail on unsupported architecture
ansible.builtin.fail:
msg: "Unsupported architecture for Bun install: {{ ansible_architecture }}"
when: bun_arch_suffix is not defined or bun_arch_suffix | length == 0

View File

@ -0,0 +1,16 @@
---
- name: Wait for Gitvana service port
ansible.builtin.wait_for:
port: "{{ gitvana_service_port }}"
host: 127.0.0.1
timeout: 60
- name: Verify local HTTP health endpoint
ansible.builtin.uri:
url: "http://127.0.0.1:{{ gitvana_service_port }}{{ gitvana_healthcheck_path }}"
method: GET
status_code: "{{ gitvana_verify_status_codes }}"
register: gitvana_health_result
retries: 3
delay: 5
until: gitvana_health_result.status in gitvana_verify_status_codes

View File

@ -0,0 +1,21 @@
[Unit]
Description=Gitvana Bun Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User={{ gitvana_app_user }}
Group={{ gitvana_app_group }}
WorkingDirectory={{ gitvana_app_root }}
{% for key, value in gitvana_env.items() %}
Environment={{ key }}={{ value }}
{% endfor %}
ExecStart=/usr/local/bin/bun run {{ gitvana_run_mode }} --host 0.0.0.0 --port {{ gitvana_service_port }}
Restart=on-failure
RestartSec=5
TimeoutStartSec=120
TimeoutStopSec=30
[Install]
WantedBy=multi-user.target