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)
|