summaryrefslogtreecommitdiff
path: root/ybd/release_note.py
blob: c3c1c93ac828ba418349899ba377f0c031b35f7a (plain)
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
# Copyright (C) 2016  Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# =*= License: GPL-2 =*=

import os
import re
from subprocess import check_output
import tempfile
import app
import yaml
import difflib
import itertools
from app import chdir, config, log
from pots import Pots
from repos import (
    explore, get_last_tag, get_repo_name, mirror, mirror_has_ref, update_mirror
)


def do_release_note(release_note):
    tempfile.tempdir = config['tmp']
    tmpdir = tempfile.mkdtemp()

    if 'release-since' in config:
        ref = config['release-since']
    else:
        ref = get_last_tag('.')

    with explore(ref) as defdir:
        old_defs = Pots()._data

    for path in app.defs._data:
        dn = app.defs.get(path)
        if dn.get('cache'):
            log_changes(dn, tmpdir, old_defs, ref)

    count = 0
    with open(release_note, 'w') as f:
        for log_file in os.listdir(tmpdir):
            count += 1
            f.write('====================================================\n\n')
            with open(os.path.join(tmpdir, log_file)) as infile:
                for line in infile:
                    f.write(line)
            f.write('\n\n')
    log('RELEASE NOTE', 'Changes for %s components logged at' % count,
        release_note)


def represent_str(dumper, data):
    if data.count('\n') == 0:
        return yaml.representer.SafeRepresenter.represent_str(dumper, data)
    return dumper.represent_scalar(u'tag:yaml.org,2002:str', data, style='|')


def sanitise_yaml(obj):
    stripped = re.sub(r' +(?=\\n)', '', yaml.dump(obj), flags=re.MULTILINE)
    yaml.add_representer(str, represent_str)
    return yaml.safe_load(stripped)


def expand_obj(obj):
    return [x for x in yaml.dump(obj, default_flow_style=False).splitlines()]


def log_changes(dn, tmpdir, old_defs, ref):
    do_git_log = False
    old_def = old_defs.get(dn['path'], {})
    log_file = os.path.join(tmpdir, dn['name'])
    with open(log_file, 'w') as f:
        keys = set(dn) - set(['tree', 'cache', 'repourl'])
        for key in keys:
            old_value = old_def.get(key)
            if dn[key] != old_value:
                f.write('[%s] Value changed: %s\n' % (dn['path'], key))
                if type(dn[key]) in (str, unicode, int):
                    f.write('%s | %s\n' % (old_value, dn[key]))
                if type(dn[key]) not in (str, unicode, int, float):
                    before = expand_obj(sanitise_yaml(old_value or ""))
                    after = expand_obj(sanitise_yaml(dn[key]))
                    diff = itertools.islice(difflib.unified_diff(
                        before, after), 2, None)
                    f.write('\n'.join(diff))
                f.write('\n\n')

        if (dn.get('kind', 'chunk') == 'chunk' and dn.get('repo') and
                config.get('release-cmd') and old_def):
            try:
                gitdir = os.path.join(config['gits'],
                                      get_repo_name(dn['repo']))
                cur_ref = dn['sha']
                old_ref = old_def.get('sha', old_def['ref'])
                if old_ref != cur_ref:
                    log(dn, 'Logging git change history', tmpdir)
                    if not os.path.exists(gitdir):
                        mirror(dn['name'], dn['repo'])
                    elif not mirror_has_ref(gitdir, cur_ref) or \
                            not mirror_has_ref(gitdir, old_ref):
                        update_mirror(dn['name'], dn['repo'], gitdir)
                    with chdir(gitdir):
                        text = old_ref + '..' + cur_ref
                        f.write("[%s] Repository changes:\n" % dn['path'])
                        f.write(check_output(config['release-cmd'] + [text]))
            except Exception:
                log(dn, 'WARNING: Failed to log git changes')
    if os.stat(log_file).st_size == 0:
        os.remove(log_file)