aboutsummaryrefslogtreecommitdiff
path: root/README.rst
blob: 1d193955577eafe18812dfafe2e53fbffdd12702 (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
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
Release Log Parser
==================
Software packages usually include textual files describing noteworthy
changes in each subsequent release. There exist several variants (or
formats) of such files.

This package provides Python framework for parsing the most often used
formats of such release log files. Support for any new format can be
easily added.

Release Logs
============
``Release Log`` is a textual file included in a software package, which
contains descriptions of existing releases of the package. Such a
file is normally included in each distributed archive of the package
and is present in its VCS repository.

Little or no effort has been invested into standartization of release
log formats. There exists a plethora of variations which differ more
or less considerably. The choice of a particular variation for a given
package depends mostly on the language this package is written in and
the distribution system adopted for this package. Authors' preferences
play certain role as well.

Despite the diversity of release log formats, similarities between
them overnumber their differences. The following observations hold true:

1. Release logs are plaintext files.
2. Within a file, each release is described by a separate entry.
3. Each such entry consists of a heading, containing at least the
   version number and date of the release, and a textual block discussing
   the changes introduced with this release.
4. Entries are arranged in reverse chronological order, the most
   recent release being described first.
5. Format of the headings is consistent throughout the given release
   log.
6. Entry description is usually a list of changes. However, more
   verbose and general descriptions may also appear within it. In
   general, it is safest to assume the description to be an opaque block
   of arbitrary text.
7. Release logs can contain additional textual information before the
   first release entry (a "prologue") and after the last release entry
   (an "epilogue").

Supported Formats
=================
Most frequently used release log formats can be grouped into three
main families:

``GNU-style`` release logs

  These are normally used by GNU software. Such log files are usually named
  "NEWS". Example heading lines are::

    version 1.30 - Sergey Poznyakoff, 2017-12-17
    Version 1.18 - 2018-08-21
    * Version 4.2, 2014-05-23

``Perl-style`` release logs

  These are the "Changes" files included in each Perl package
  distributed via CPAN. Example heading lines::

    2.00 2018-03-08
    1.01 Sat Jul  7 19:11:35 2018  

``Python package`` release logs

  The "CHANGES.txt" files found in many Python packages. Example heading
  lines:

    v2.0.1, 2014/12/14 -- Update token generator
    2.7 (23 June 2018)

  The special feature of the first heading variant is that the first
  line of the changeset description follows the heading on the same
  physical line. Quite often this is the only line in the description.

Usage
=====
The ``ReleaseLog`` class is a fabric returning actual release history
implementation, depending on the first argument to its constructor.
Typical usage::

      rl = ReleaseLog('GNU', content, count=1)

The two mandatory arguments are the format name and the list of lines
obtained from the release log file.

Valid format names for this version of ``releaselogparser`` are:

``GNU``, ``NEWS``
  GNU-style news file.
``CPAN``, ``Changes``
  Perl-style release log.
``Python``, ``python``
  Python-style release log.

Supported keyword arguments are:

start = *N*
  Start parsing from the entry *N*. Entries are numbered from 0.
stop = *N*
  Stop parsing on the entry *N*.
count = *N*
  Collect at most *N* entries

If all three keywords are given, the actual range of history entries
is computed as

  [start, min(start+count, stop)]

Two derived classes are provided that read input data from various
sources:

class ``ReleaseLogFile``
------------------------
The ``ReleaseLogFile`` class reads release log from the file::

  rl = ReleaseLogFile(fmt, file [, kwargs...])

Here, ``fmt`` is the name of the format, ``file`` is the name of the
input file, and ``kwargs`` are keyword arguments described above.

class ``ReleaseLogURL``
-----------------------
The ``ReleaseLogURL`` class reads log entries from a URL::

  rl = ReleaseLogURL(fmt, url [, kwargs...])

Acessing release information
----------------------------
The returned object can be indexed to obtain particular log
entries. Indices start with 0, which corresponds to the most recent
entry, e.g.:

  entry = cl[0]

The ``entry`` is an object of class ``Release``, which has three
attributes:

``version``
  Release version number.
``date``
  Date and time of the release (a datetime object)
``descr``
  Textual description of the release - a list of lines.

The obtained entry can be printed as string, e.g.:

  print(entry)

The output format is as shown in the example below:

  Version 1.0, released at 2018-08-19 15:30:00

Example
=======
The following simple program reads release log entries from the file
``NEWS`` and prints them on the standard output::

  from releaselogparser.input import ReleaseLogFile

  for log in ReleaseLogFile('GNU', 'NEWS'):
      print(log)
      print('\n'.join(log.descr))

Extending Release Log
=====================
Implementing support for new release log format is fairly easy. To do
so, provide a class inherited from ``ReleaseHistory``. This base class has
the following attributes:

``format``
  List of names for this format. Names from this list can be used
  interchangeably to identify this log format, e.g. as a first
  argument to the ``ReleaseLog`` or derived constructor.
``filename``
  Name of the file used normally for release logs in this format.
``header``
  Compiled regular expression that returns a match for
  history entry heading lines. The expression must contain two named
  groups: ``version``, which returns part of the string corresponding
  to the release version number, and ``date``, returning its
  timestamp.

  If it contains a named group ``rest``, part of the header string
  corresponding to this group will be added to the ``descr`` list of
  the created history entry.

``end_of_entry_rx``
  Compiled regular expression that matches end of entry. Can be
  ``None``, if not needed.

The file with the definition of the inherited class must be placed in
the directory ``releaselogparser/format`` reachable from the Python search path
for module files.

The following example implements a simplified version of CHANGES.txt log
format::

  import re
  from releaselogparser import ReleaseHistory

  class ChangesLogFormat(ReleaseHistory):
      format = ['changes']
      filename = 'CHANGES.txt'
      header = re.compile("""^[vV](?P<version>\d[\d.]*)\s*
                          ,\s*
                          (?P<date>.*?)
                          \s+-+\s*
                          (?P<rest>.*)$
                          """, re.X)

More sophisticated implementations can overload the ``parse_header``
method of the parent class. This method is defined as follows::

  def parse_header(self, line):

If the input ``line`` is an entry header, the method should return
a triplet::

  (date, version, first_line)

where ``date`` is textual representation of the date of the release,
``version`` is the release version string, and ``first_line`` is the
first line of the description (can be None).

If the line is not a valid entry header, the method returns
``(None, None, None)``.


The ``releaselog`` utility
==========================
The ``releaselog`` tool reads release logs in various formats from a
given file or URL. Its usage is::

 releaselog [OPTIONS] FILE-or-URL

The argument is treated as file name by default. To read from a URL,
use the ``--url`` option.

Options:

``-H FORMAT``, ``--format=FORMAT``
  Read logs in the given format.
``-f N``, ``--from=N``, ``--start=N``
  Start from *N* th entry.
``-t N``, ``--to=N``, ``--stop=N``
  End on *N* th entry.
``-n COUNT``, ``--count=COUNT``
  Read at most that much entries.
``-u``, ``--url``
  Treat argument as URL
``-l``, ``--list``
  List supported formats
``--version``
  Show program version number and exit.
``-h``, ``--help``
  Show a short help message and exit.

Return to:

Send suggestions and report system problems to the System administrator.