aboutsummaryrefslogtreecommitdiffstats
path: root/git_deps/server.py
blob: 71859927e05e4caae77c146361f90947f1b04309 (plain) (blame)
1
2
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import os
import subprocess
import sys

from git_deps.gitutils import GitUtils
from git_deps.detector import DependencyDetector
from git_deps.errors import InvalidCommitish
from git_deps.listener.json import JSONDependencyListener
from git_deps.utils import abort, standard_logger


def serve(options):
    try:
        import flask
        from flask import Flask, send_file
        from flask.json import jsonify
        from werkzeug.security import safe_join
    except ImportError:
        abort("Cannot find flask module which is required for webserver mode.")

    logger = standard_logger(__name__, options.debug)

    webserver = Flask('git-deps')
    here = os.path.dirname(os.path.realpath(__file__))
    root = os.path.join(here, 'html')
    webserver.root_path = root
    logger.debug("Webserver root is %s" % root)

    ##########################################################
    # Static content

    @webserver.route('/')
    def main_page():
        return send_file('git-deps.html')

    @webserver.route('/tip-template.html')
    def tip_template():
        return send_file('tip-template.html')

    @webserver.route('/test.json')
    def data():
        return send_file('test.json')

    def make_subdir_handler(subdir):
        def subdir_handler(filename):
            path = safe_join(root, subdir)
            path = safe_join(path, filename)
            if os.path.exists(path):
                return send_file(path)
            else:
                flask.abort(404)
        return subdir_handler

    for subdir in ('node_modules', 'css', 'js'):
        fn = make_subdir_handler(subdir)
        route = '/%s/<path:filename>' % subdir
        webserver.add_url_rule(route, subdir + '_handler', fn)

    ##########################################################
    # Dynamic content

    def json_error(status_code, error_class, message, **extra):
        json = {
            'status': status_code,
            'error_class': error_class,
            'message': message,
        }
        json.update(extra)
        response = jsonify(json)
        response.status_code = status_code
        return response

    @webserver.route('/options')
    def send_options():
        client_options = options.__dict__
        client_options['repo_path'] = os.getcwd()
        return jsonify(client_options)

    @webserver.route('/deps.json/<revspec>')
    def deps(revspec):
        detector = DependencyDetector(options)
        listener = JSONDependencyListener(options)
        detector.add_listener(listener)

        if '..' in revspec:
            try:
                revisions = GitUtils.rev_list(revspec)
            except subprocess.CalledProcessError:
                return json_error(
                    422, 'Invalid revision range',
                    "Could not resolve revision range '%s'" % revspec,
                    revspec=revspec)
        else:
            revisions = [revspec]

        for rev in revisions:
            try:
                detector.get_commit(rev)
            except InvalidCommitish:
                return json_error(
                    422, 'Invalid revision',
                    "Could not resolve revision '%s'" % rev,
                    rev=rev)

            detector.find_dependencies(rev)

        tip_commit = detector.get_commit(revisions[0])
        tip_sha1 = tip_commit.hex

        json = listener.json()
        json['query'] = {
            'revspec': revspec,
            'revisions': revisions,
            'tip_sha1': tip_sha1,
            'tip_abbrev': GitUtils.abbreviate_sha1(tip_sha1),
        }
        return jsonify(json)

    # We don't want to see double-decker warnings, so check
    # WERKZEUG_RUN_MAIN which is only set for the first startup, not
    # on app reloads.
    if options.debug and not os.getenv('WERKZEUG_RUN_MAIN'):
        print("!! WARNING!  Debug mode enabled, so webserver is completely "
              "insecure!")
        print("!! Arbitrary code can be executed from browser!")
        print()
    try:
        webserver.run(port=options.port, debug=options.debug,
                      host=options.bindaddr)
    except OSError as e:
        print("\n!!! ERROR: Could not start server:")
        print("!!!")
        print("!!!   " + str(e))
        print("!!!")
        if e.strerror == "Address already in use":
            print("!!! Do you already have a git deps server running?")
            print("!!! If so, stop it first and try again.")
            print("!!!")
        print("!!! Aborting.")
        sys.exit(1)