summaryrefslogtreecommitdiffstats
path: root/gd_diff.py
blob: ac79d20c4ce02037c7371531592c30839b25c239 (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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
#  This file is part of gns-deb-diff.
#
#  gns-deb-diff is under the Public Domain. See
#  <https://creativecommons.org/publicdomain/zero/1.0>

import argparse
import json
import os
import re
import shlex
import sys

import requests

from os import path
from subprocess import run, PIPE

from bs4 import BeautifulSoup

_version = '0.1.0dev0'

# list of recognized fields.
field_list = [
    "Change-Type",
    "Changed-From-Debian",
]

# bzr
bzr_base_url = 'bzr://bzr.savannah.gnu.org/gnewsense/'
readme_url_fmt = '%s/packages-{}/{}/debian/README.gNewSense' % bzr_base_url


def read_file(fpath):
    """Read file `f` and return its content.

    """
    try:
        f = open(fpath, 'r')
    except FileNotFoundError as e:
        print("Error opening '%s' \n Error Info:\n  %r" % (fpath, e),
              file=sys.stderr)
        sys.exit(1)

    return f.read()


def write_file(fpath, content):
    """Write `content` to file `fpath`.

    """
    try:
      f = open(fpath, 'w')
      f.write(content)
      f.close()
    except IOError as e:
        print('Error creating and writing content to {}\n {}'.format(
            fpath, e))
        exit(1)


def execute(cmd, out=None, err=None):
    """Run `cmd`. Returns an instance of `subprocess.CompletedProcess`

    `cmd` must be a string containing the command to run.
    """
    cmd = shlex.split(cmd)

    try:
        completed_process = run(cmd, stdout=out, stderr=err)
    except (FileNotFoundError, OSError, ValueError) as e:
        print("Error running '%s'\n Error Info:\n %r" % (cmd[0], e),
              file=sys.stderr)
        sys.exit(1)

    return completed_process


def read_packages(pkgs_file):
    """Return an iterator contaning of package names from `pkgs_file`.

    """
    pkgs = read_file(pkgs_file).split('\n')
    # sanitize
    pkgs_iter = map(lambda x: x.strip(), pkgs)

    return pkgs_iter


def get_packages(release):
    """Return newline separated list of packages for `release`.

    List of packages is slurped from
        http://bzr.savannah.gnu.org/lh/gnewsense/packages-`release`
    """
    url = 'http://bzr.savannah.gnu.org/lh/gnewsense/packages-{}/'
    req = url.format(release)

    try:
        res = requests.get(req)
    except ConnectionError as ce:
        print('ERROR: Problem GETting {} \n{}'.format(req, ce))
        sys.exit(1)

    if res.status_code != 200:
        print('{}: Error GETting {}'.format(res.status_code, req))
        sys.exit(1)

    html_forest = BeautifulSoup(res.text, 'html.parser')

    pkgs = '' # newline separated list of pkgs.
    for td in html_forest.find_all('td', class_='autcell'):
        pkgs += td.a.string.strip() + '\n'

    return pkgs


def config_dir():
    """Return the gns-deb-diff config directory.

    As a side effect, the directory is created if it does not exist.

    """
    cd = os.path.join(os.getenv('HOME'), '.config', 'gns-deb-diff')

    if not os.path.isdir(cd):
        os.makedirs(cd)

    return cd


def config_file():
    return os.path.join(config_dir(), 'config')


def read_config_file():
    """Return config as a Python Object; False when config does not \
    exist.

    """
    cf = config_file()

    if not os.path.isfile(cf):
        return False

    return json.load(open(cf, 'r'))


def pkgs_dir():
    """Return the `pkgs` directory.

    As a side effect, the directory is created if it does not exist.
    """
    pd = os.path.join(config_dir(), 'pkgs')
    if not os.path.isdir(pd):
        os.mkdir(pd)

    return pd


def mk_pkgs_list(release):
    """Get pkgs for release and write to disk.

    It gets written to `~/.config/pkgs/release`.

    """
    pkgs = get_packages(release)
    pkgs_file = os.path.join(pkgs_dir(), release)
    write_file(pkgs_file, pkgs)

    return pkgs_file


def readmes_dir(release):
    """Return readmes directory for `release`.

    As a side effect, the directory is created if it does not exist.
    """
    rd = os.path.join(config_dir(), 'readmes')
    if not os.path.isdir(rd):
        os.mkdir(rd)

    rd_release = os.path.join(rd, release)
    if not os.path.isdir(rd_release):
        os.mkdir(rd_release)

    return rd_release


def wiki_page_dir(release):
    """Get wiki page directory for `release`.
    """
    wd = os.path.join(config_dir(), 'wiki-page')

    if not os.path.isdir(wd):
        os.mkdir(wd)

    wd_release = os.path.join(wd, release)
    if not os.path.isdir(wd_release):
        os.mkdir(wd_release)

    return wd_release


def write_wiki_page(release, content):
    """Write wiki page `content` to `release`' last.rev file.

    """
    wd_release = wiki_page_dir(release)
    wp_file = os.path.join(wd_release, 'last.rev')
    write_file(wp_file, content)


def configured_p():
    """Returns True if gns-deb-diff is configured; False otherwise.
    """
    if os.path.isfile(config_file()):
        return True
    else:
        return False


def configure():
    """Configure gns-deb-diff.
    """
    # prompt username and password.
    config = {}
    config['user'] = input('gNewSense wiki username: ')
    config['pass'] = input('gNewSense wiki password: ')

    json.dump(config, open(config_file(), 'w'))
    os.chmod(config_file(), mode=0o600)


def save_gns_readme(content, release, pkg):
    """Save README.gNewsense locally.

    :param str content:
        Content of the README.gNewsense file.
    :param str release:
        Release name.
    :param str pkg:
        Package name.
    """
    # create gns_readme dir. for pkg.
    gns_readme_dir = path.join(readmes_dir(release), pkg, 'debian')

    try:
        os.makedirs(gns_readme_dir, exist_ok=True)
    except Exception as e:
        print("Error creating directory '%s'\n Error Info:\n %r" %
              (gns_readme_dir, e), file=sys.stderr)
        sys.exit(1)

    gns_readme = path.join(gns_readme_dir, 'README.gNewSense')
    write_file(gns_readme, content)
    print('Saved {}'.format(gns_readme))


def slurp_gns_readme(release, pkg):
    """Read and save the README.gNewSense for `pkg` in `release`.

    """
    readme_url = readme_url_fmt.format(release, pkg)
    cmd = 'bzr cat {}'.format(readme_url)
    cp = execute(cmd, out=PIPE, err=PIPE)

    if(cp.returncode == 0):
        save_gns_readme(cp.stdout.decode(), release, pkg)
        return True
    else:
        print("README.gNewSense not found for package {}".format(pkg),
              file=sys.stderr)
        return False


def slurp_all_gns_readmes(release, pkgs):
    """Read and save all README.gNewSense for `pkgs` in `release`.

    Returns list of packages in `pkgs` that does not have README.gNewSense.
    """
    pkgs_noreadmes = []
    for pkg in pkgs:
        slurped = slurp_gns_readme(release, pkg)

        if(not slurped):
            pkgs_noreadmes.append(pkg)

    return pkgs_noreadmes


def read_gns_readme(release, pkg):
    """Returns content of README.gNewSense for `pkg`.

    If `README.gNewSense` does not exists for `pkg`, None is returned.

    """
    readme_path = path.join(readmes_dir(release), pkg, 'debian',
                            'README.gNewSense')

    if not path.isfile(readme_path):
        return None

    readme_content = read_file(readme_path)
    return readme_content


def slurp_fields_from_readme(content):
    """Returns dict containing fields slurped from `content`

    - If a field is not defined or if its value is empty in the
    `content`, then its corresponding value in the dict will be None.

    """
    field_values = {}
    for field in field_list:
        pattern = r'{}:[ ]*(.+)'.format(field)
        field_pattern = re.compile(pattern)
        field_match = field_pattern.search(content)

        if (field_match and
            field_match.group(1) and
            field_match.group(1).strip()):
            field_values[field] = field_match.group(1).strip()
        else:
            field_values[field] = None

    return field_values