summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
Diffstat (limited to 'source')
-rw-r--r--source/package_manifest.py82
-rw-r--r--source/release_notes.py181
2 files changed, 263 insertions, 0 deletions
diff --git a/source/package_manifest.py b/source/package_manifest.py
new file mode 100644
index 0000000..47c5bef
--- /dev/null
+++ b/source/package_manifest.py
@@ -0,0 +1,82 @@
+import json
+from collections import OrderedDict
+from urllib.request import urlopen
+from pprint import pprint
+
+ARCHITECTURE = [ 'linux-64', 'osx-64']
+METAPACKAGES = [('stsci', '1.0.0'),
+ ('stsci-data-analysis', '1.0.0'),
+ ('stsci-hst', '1.0.0'),
+ ('stsci-jwst', '1.0.0')]
+REPODATA_URL='http://ssb.stsci.edu/astroconda/{arch}/repodata.json'
+#REPODATA_URL='http://ssb.stsci.edu/conda-dev/{arch}/repodata.json'
+
+MESSAGE = """
+Packaging reference key:
+
+::
+
+ [package]-[version]-[glob]_[build_number]
+ ^Name ^Version ^ ^Conda package revision
+ |
+ npXXpyYY
+ ^ ^
+ | |
+ | Compiled for Python version
+ |
+ Compiled for NumPY version
+
+
+"""
+
+def get_repodata(architecture):
+ """ Retrieves repository data but strips the info key (there's never anything there)
+ """
+ url = REPODATA_URL.format(arch=architecture)
+ with urlopen(url) as response:
+ data = OrderedDict(json.loads(response.read().decode())['packages'])
+
+ return data
+
+
+if __name__ == '__main__':
+
+ python_versions = dict(
+ py27='2.7',
+ py34='3.4',
+ py35='3.5'
+ )
+
+ with open('package_manifest.rst', 'w+') as pfile:
+ print('Packages', file=pfile)
+ print('========\n\n', file=pfile)
+ print('{0}\n\n'.format(MESSAGE), file=pfile)
+
+ repo_data = OrderedDict()
+ for arch in ARCHITECTURE:
+ repo_data[arch] = get_repodata(arch)
+
+ metapackages = []
+ for mpkg, mpkg_version in METAPACKAGES:
+ for key, value in repo_data[arch].items():
+ if mpkg == repo_data[arch][key]['name']:
+ if mpkg_version == repo_data[arch][key]['version']:
+ metapackages.append(('-'.join([value['name'], value['version']]), value['build'], value['depends']))
+
+ print('{arch} metapackages'.format(arch=arch), file=pfile)
+ print('------------------------\n\n', file=pfile)
+
+ metapackages = sorted(metapackages, key=lambda k: k[0])
+ for name, build, dependencies in metapackages:
+ print('- **{name} ({python})**\n'.format(name=name, python=build), file=pfile)
+ for pkg in dependencies:
+ print(' - {:<20s}\n'.format(pkg), file=pfile)
+
+ print('{arch} packages'.format(arch=arch), file=pfile)
+ print('------------------------\n\n', file=pfile)
+
+ _packages = sorted(repo_data[arch].values(), key=lambda k: k['name'])
+ packages = set(['-'.join([d['name'], d['version']]) for d in _packages])
+
+ for record in sorted(packages):
+ print('- {name}\n'.format(name=record, header='-' * len(record)), file=pfile)
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)