summaryrefslogtreecommitdiff
path: root/source/release_notes.py
diff options
context:
space:
mode:
authorJoseph Hunkeler <jhunkeler@gmail.com>2016-04-29 15:51:36 -0400
committerJoseph Hunkeler <jhunkeler@gmail.com>2016-04-29 15:51:36 -0400
commit7e042a1fadb6199096ed6e2343798200fec8a6df (patch)
tree538eb50f44856c6c8e5fd805b58da75c9a5f9ecc /source/release_notes.py
parente5444af336caaa75e62eeb21701e1a43a1eb88f6 (diff)
downloadastroconda-7e042a1fadb6199096ed6e2343798200fec8a6df.tar.gz
Initial commit of release_notes generator
Diffstat (limited to 'source/release_notes.py')
-rw-r--r--source/release_notes.py181
1 files changed, 181 insertions, 0 deletions
diff --git a/source/release_notes.py b/source/release_notes.py
new file mode 100644
index 0000000..3c1b397
--- /dev/null
+++ b/source/release_notes.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import os
+import math
+from collections import OrderedDict
+
+try:
+ from github3 import login
+ from github3 import GitHubError
+except ImportError:
+ print('github3.py required.')
+ print('pip install https+git://github.com/sigmavirus24/github3.py')
+ exit(1)
+
+try:
+ import pypandoc
+except ImportError:
+ print('pypandoc required.')
+ print('conda install -c https://conda.anaconda.org/janschulz pypandoc')
+ exit(1)
+
+
+header = """
+# Release Notes
+"""
+title = """
+## {name}
+"""
+message = """
+### {title}
+
+*{date}*
+
+{body}
+
+"""
+
+
+def explode_newlines(s):
+ """Normalize newlines and double them up for MD->RST conversion.
+ """
+ tmp = ''
+ for line in s.splitlines():
+ line = line.replace('\r\n', '\n')
+ line = line + '\n\n'
+ tmp += line
+
+ return tmp
+
+def transform_headings(s):
+ """Trust no one.
+ Header levels less than or equal to 3 are up-converted.
+
+ 1 = 4
+ 2 = 4
+ 3 = 5
+ 4 = 6
+ 5 = 6
+ 6 = 6
+
+ Oh well, sucks right?
+ """
+ delim = '#'
+ headings = OrderedDict(
+ h6=delim * 6,
+ h5=delim * 5,
+ h4=delim * 4,
+ h3=delim * 3,
+ h2=delim * 2,
+ h1=delim * 1
+ )
+ output = ''
+
+ for line in s.splitlines():
+ length = len(line)
+ stop = len(headings.keys()) + 1
+
+ if length < stop:
+ stop = length
+
+ block = line[:stop]
+ depth = 0
+ for i, ch in enumerate(block, 1):
+ if ch != '#': break
+ depth = i
+
+ if depth == 0 or depth > 3:
+ output += explode_newlines(line)
+ continue
+
+ #print('length={:<10d} stop={:<10d} depth={:<10d} block={:>20s}'.format(length, stop, depth, block))
+
+ current = headings['h'+str(depth)]
+ try:
+ required = headings['h'+str(math.ceil(depth + depth / depth + 1))]
+ except KeyError:
+ required = headings['h'+str(len(headings) - 1)]
+
+ if depth > 0 and depth <= 3:
+ print("Heading levels 1, 2, and 3 are allocated by this script...")
+ print("Offending line: '{0}'".format(line))
+ print("Automatically converted to: '{0}'".format(required))
+ print("(FIX YOUR RELEASE NOTES)\n\n")
+
+ #print('Transformed "{0}" -> "{1}"'.format(current, required))
+ line = line.replace(current, required)
+ output += explode_newlines(line)
+
+ return output
+
+def read_token(filename):
+ """For the sake of security, paste your github auth token in a file and reference it using this function
+
+ Expected file format (one-liner):
+ GITHUB_USER_AUTH_TOKEN_HERE
+ """
+ with open(filename, 'r') as f:
+ # Obtain the first line in the token file
+ token = f.readline().rstrip()
+
+ if not token:
+ return None
+
+ for i, ch in enumerate(token):
+ if not ch.isalnum():
+ raise ValueError('Invalid token: Non-alphanumeric character detected (Value: "{0}", ASCII: {1}, Index: {2}) '.format(ch, ord(ch), i))
+
+ return token
+
+
+def pull_release_notes(tmpfile, outfile):
+ with open(tmpfile, 'w+') as mdout:
+ # Write header (main title)
+ print(header, file=mdout)
+
+ # Sort repositories inline alphabetically, because otherwise its seemingly random order
+ for repository in sorted(list(org.repositories()), key=lambda k: k.as_dict()['name']):
+ repo = repository.as_dict()
+ repo_name = repo['name'].upper()
+
+ # Ignore repositories without release notes
+ releases = list(repository.releases())
+ if not releases:
+ continue
+
+ # Write repository title
+ print(title.format(name=repo_name), file=mdout)
+
+ for note in repository.releases():
+ # RST is EXTREMELY particular about newlines.
+ body = explode_newlines(note.body)
+ body = transform_headings(note.body)
+ # Write release notes structure
+ print(message.format(title=note.name,
+ date=note.published_at,
+ body=body), file=mdout)
+
+
+if __name__ == '__main__':
+ owner = 'spacetelescope'
+ tmpfile = 'release_notes.md'
+ outfile = 'release_notes.rst'
+
+ gh = login(token=read_token('TOKEN'))
+ org = gh.organization(owner)
+
+ try:
+ pull_release_notes(tmpfile, outfile)
+ except GitHubError as e:
+ print(e)
+ exit(1)
+
+ if os.path.exists(outfile):
+ os.unlink(outfile)
+
+ if os.path.exists(tmpfile):
+ pypandoc.convert(source=tmpfile,
+ to='rst',
+ outputfile=outfile)
+ os.unlink(tmpfile)