deploy.py 4.01 KB
Newer Older
Enrico Zini's avatar
Enrico Zini committed
1
#!/usr/bin/python3
Enrico Zini's avatar
Enrico Zini committed
2
from django.core.management.base import BaseCommand
Enrico Zini's avatar
Enrico Zini committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from django.conf import settings
from django.contrib.sites.models import Site
from deploy.deployer import Fail, Deployer
import os
import sys
import smtplib
import email.message
import logging
import logging.handlers

log = logging.getLogger("deploy")


class BufferedEmailHandler(logging.handlers.BufferingHandler):
    """
    Buffer log lines and send a single mail at the end.

    Wrapping SMTPHandler in a MemoryHandler seemed like the right way of doing
    this, but what it really seems to do is still send an email per log
    message, but at flush time instead of when logging is called. *headdesks*
    """
    def __init__(self, fromaddr, toaddrs, subject):
        super().__init__(0)
        self.mailport = smtplib.SMTP_PORT
        self.mailhost = "localhost"
        self.fromaddr = fromaddr
        self.toaddrs = toaddrs
        self.subject = subject

    def shouldFlush(self, record):
        # Disable useless capacity checks (python bug #32934)
        return False

    def flush(self):
        if not self.buffer: return
        self.acquire()
        try:
            # Format the records
            lines = []
            for record in self.buffer:
                try:
                    lines.append(self.format(record))
                except:
                    self.handleError(record)

            msg = email.message.EmailMessage()
            msg["From"] = self.fromaddr
            msg["To"] = self.toaddrs
            msg["Subject"] = self.subject
            msg.set_content("\n".join(lines))

            try:
                smtp = smtplib.SMTP(self.mailhost, self.mailport)
                smtp.send_message(msg)
                smtp.quit()
            except:
                self.handleError(None)  # no particular record
            self.buffer = []
        finally:
            self.release()


# TODO:
#  - ./manage.py commands can be invoked using django standard ways
#  - custom deploy commands can be configured in settings


class Command(BaseCommand):
    help = "Deploy a signed git commit"

    def add_arguments(self, parser):
        parser.add_argument("--dry-run", action="store_true", help="do everything except write operations")

    def handle(self, *args, **options):
        log_format = "%(asctime)-15s %(levelname)s %(message)s"

        branch = getattr(settings, "DEPLOY_BRANCH", "origin/master")
        queue_dir = getattr(settings, "DEPLOY_QUEUE_DIR", None)

        site = Site.objects.get_current()
        email_handler = BufferedEmailHandler(
                fromaddr="unknown@example.org",
                toaddrs=[x[1] for x in settings.ADMINS],
                subject="{} deploy of {}".format(site.domain, branch))
        email_handler.setLevel(logging.INFO)
        email_handler.setFormatter(logging.Formatter(log_format))
        log.addHandler(email_handler)

        stream_handler = logging.StreamHandler(stream=sys.stderr)
        if options["verbosity"] == 0:
            stream_handler.setLevel(level=logging.ERROR)
        elif options["verbosity"] == 1:
            stream_handler.setLevel(level=logging.WARNING)
        elif options["verbosity"] == 2:
            stream_handler.setLevel(level=logging.INFO)
        elif options["verbosity"] == 3:
            stream_handler.setLevel(level=logging.DEBUG)
        stream_handler.setFormatter(logging.Formatter(log_format))
        log.addHandler(stream_handler)

        deploy_dir = os.path.abspath(os.path.dirname(sys.argv[0]))
        os.chdir(deploy_dir)

        try:
107
            deployer = Deployer(dry_run=options["dry_run"])
Enrico Zini's avatar
Enrico Zini committed
108
            deployer.select_branch()
109
            email_handler.fromaddr = deployer.ref.commit.committer.email
Enrico Zini's avatar
Enrico Zini committed
110
111
112
113
114
115
116
117
            log.debug("Email from: %s", email_handler.fromaddr)
            log.debug("Email to: %r", email_handler.toaddrs)
            log.debug("Email subject: %s", email_handler.subject)
            deployer.validate_commit()
            deployer.deploy()
            deployer.cleanup()
        except Fail as e:
            log.error("%s", e)