Snippets

Anton Shestakov hgweb-lighttpd-ansible

Created by Anton Shestakov last modified
---
- hosts: all
  become: yes
  vars:
    hostname: mydomain
  pre_tasks:
    # NOTE: you probably don't want to run these pre_tasks on a real box. Just
    # bear in mind that to be able to access hgweb from inside the box, you
    # need to use the virtual host name (i.e. http://127.0.0.1/ won't work).
    # So make sure you either have DNS records for hg.mydomain or it's in
    # /etc/hosts.
    - name: Set hostname
      hostname:
        name: '{{ hostname }}'
    - name: Update /etc/hosts
      lineinfile:
        dest: /etc/hosts
        line: '127.0.0.1 {{ hostname }} hg.{{ hostname }}'

    # NOTE: hgweb role depends on Lighttpd and uWSGI being installed. It's
    # recommended to install them in separate lighttpd and uwsgi roles, not in
    # pre_tasks like in this example playbook.
    - name: Install Lighttpd
      apt:
        update_cache: yes
        cache_valid_time: 3600
        package: lighttpd
        state: installed
    - name: Install uWSGI
      apt:
        package: '{{ item }}'
        state: installed
      with_items:
        - uwsgi
        - uwsgi-plugin-python
  roles:
#    - role: lighttpd
#      tags: [lighttpd]
#    - role: uwsgi
#      tags: [uwsgi]
    - role: hgweb
      tags: [hgweb]
  handlers:
    # NOTE: hgweb role notifies two handlers:
    # - restart uwsgi
    # - restart lighttpd
    # Naturally, the handlers are not defined in the role itself, so this
    # example playbook includes them here.
    - name: restart uwsgi
      service:
        name: uwsgi
        state: restarted
    - name: restart lighttpd
      service:
        name: lighttpd
        state: restarted
...
# NOTE: how to test that it works correctly (also see the note about DNS):
# curl -I http://hg.mydomain/
# curl -I http://hg.mydomain/static/mercurial.js
#   then check /var/log/uwsgi/app/50-hgweb.log for problems
#   make sure it doesn't see requests /static/ (Lighttpd should handle them)
---
hgweb_contact: 'Me Myself <me@mydomain>'
hgweb_subdomain: hg
hgweb_user: hguser
hgweb_mercurial_url: https://www.mercurial-scm.org/repo/hg
hgweb_mercurial_rev: stable
hgweb_evolve_url: https://www.mercurial-scm.org/repo/evolve
hgweb_evolve_rev: stable
hgweb_workers: 1
...
User-agent: *

# Expensive pages
Disallow: /*/annotate/
Disallow: /*/comparison/
Disallow: /*/diff/
Disallow: /*/archive/

# File log is too much
Disallow: /*/log/*/

# API is off-limits
Disallow: /*/json-*
Disallow: /*/raw-*

# Paginate instead
Disallow: /*?revcount=

# Graph page is a more expensive log
Disallow: /*/graph$
Disallow: /*/graph/

# It's for humans
Disallow: /*/help$
Disallow: /*/help/
---
- name: make local
  command: make --directory /home/{{ hgweb_user }}/hg/ local
  become: yes
  become_user: '{{ hgweb_user }}'

- name: restart hgweb
  file:
    path: /home/{{ hgweb_user }}/hgwebfiles/hgweb.wsgi
    state: touch
...
---
- name: Ensure required directories exist
  file:
    path: /home/{{ hgweb_user }}/{{ item }}/
    state: directory
  with_items:
    - hgwebfiles
    - repos

- name: Pull Mercurial {{ hgweb_mercurial_rev }}
  hg:
    repo: '{{ hgweb_mercurial_url }}'
    dest: /home/{{ hgweb_user }}/hg/
    revision: '{{ hgweb_mercurial_rev }}'
  notify:
    - make local
    - restart hgweb
  tags: [inert]

- name: Pull Evolve {{ hgweb_evolve_rev }}
  hg:
    repo: '{{ hgweb_evolve_url }}'
    dest: /home/{{ hgweb_user }}/evolve/
    revision: '{{ hgweb_evolve_rev }}'
  notify:
    - restart hgweb
  tags: [inert]

- name: Add config files
  template:
    src: '{{ item }}'
    dest: /home/{{ hgweb_user }}/hgwebfiles/{{ item }}
  with_items:
    - hgweb.wsgi
    - hgweb.conf
  notify:
    - restart hgweb

- name: Copy robots.txt
  copy:
    src: robots.txt
    dest: /home/{{ hgweb_user }}/hgwebfiles/robots.txt
    mode: 0644
  tags: [robots]
...
---
- name: Create user
  user:
    name: '{{ hgweb_user }}'

- name: Install packages
  apt:
    pkg: '{{ item }}'
    state: present
  with_items:
    - build-essential
    - mercurial
    - python-dev
    - python-pygments  # for hightlight extension
  tags: [packages, inert]

- include: appinstall.yml
  become: yes
  become_user: '{{ hgweb_user }}'

- name: Add uWSGI app
  template:
    src: etc/uwsgi/apps-available/hgweb.yaml
    dest: /etc/uwsgi/apps-available/hgweb.yaml
  notify:
    - restart uwsgi

- name: Enable uWSGI app
  file:
    src: /etc/uwsgi/apps-available/hgweb.yaml
    dest: /etc/uwsgi/apps-enabled/50-hgweb.yaml
    state: link
  notify:
    - restart uwsgi

- name: Add {{ hgweb_subdomain }}.{{ hostname }} Lighttpd site
  template:
    src: etc/lighttpd/conf-available/hgweb.conf
    dest: /etc/lighttpd/conf-available/{{ hgweb_subdomain }}.{{ hostname }}.conf
  notify:
    - restart lighttpd

- name: Enable {{ hgweb_subdomain }}.{{ hostname }} Lighttpd site
  file:
    src: /etc/lighttpd/conf-available/{{ hgweb_subdomain }}.{{ hostname }}.conf
    dest: /etc/lighttpd/conf-enabled/50-{{ hgweb_subdomain }}.{{ hostname }}.conf
    state: link
  notify:
    - restart lighttpd
...
[paths]
/ = /home/{{ hgweb_user }}/repos/*

[extensions]
highlight =
evolve.serveronly = /home/{{ hgweb_user }}/evolve/hgext3rd/evolve/serveronly.py

[web]
baseurl = http://{{ hgweb_subdomain }}.{{ hostname }}/
logourl = http://{{ hgweb_subdomain }}.{{ hostname }}/
staticurl = /static
contact = {{ hgweb_contact }}
pygments_style = tango
highlightfiles = size('<100k')
highlightonlymatchfilename = True
# Path to repo or hgweb config to serve (see 'hg help hgweb')
config = "/home/{{ hgweb_user }}/hgwebfiles/hgweb.conf"

# Uncomment and adjust if Mercurial is not installed system-wide
# (consult "installed modules" path from 'hg debuginstall'):
import sys; sys.path.insert(0, "/home/{{ hgweb_user }}/hg/")

# Uncomment to send python tracebacks to the browser if an error occurs:
#import cgitb; cgitb.enable()

# enable demandloading to reduce startup time
from mercurial import demandimport; demandimport.enable()

from mercurial.hgweb import hgweb
application = hgweb(config)
server.modules += (
    # "mod_redirect",
    # "mod_alias",
    "mod_proxy",
)

$HTTP["host"] == "{{ hgweb_subdomain }}.{{ hostname }}" {
    server.document-root = "/home/{{ hgweb_user }}/hg/mercurial/templates/"

    $HTTP["url"] !~ "^/static/|^/robots.txt$" {
        # mod_proxy uses "host" instead of "socket", because of course
        proxy.server = ( "" => (( "host" => "/home/{{ hgweb_user }}/hgwebfiles/socket" )) )
    }

    alias.url = (
        "/robots.txt" => "/home/{{ hgweb_user }}/hgwebfiles/robots.txt"
    )

    url.redirect-code = 301
    url.redirect = (
        "/favicon.ico" => "/static/hgicon.png"
    )
}
uwsgi:
  chdir: /home/{{ hgweb_user }}/hgwebfiles/
  wsgi-file: hgweb.wsgi
  touch-reload: hgweb.wsgi
  uid: {{ hgweb_user }}
  # PATH_INFO is empty when using uwsgi protocol?
  http-socket: socket
  processes: {{ hgweb_workers }}
  master: true
  # max-requests: 100

Comments (0)