Add a script to automatically generate both changelogs from a single file
This avoids the need to manually copy entries between NEWS.rst and NEWS-dev.rst, and also helps fix some inconsistencies automatically. Pre-0.44.07 changelogs have not been converted yet.develop
							parent
							
								
									573fcb4bd0
								
							
						
					
					
						commit
						476483adb3
					
				@ -0,0 +1,14 @@
 | 
			
		||||
.. comment
 | 
			
		||||
    This is the changelog for stable releases. Entries are included from
 | 
			
		||||
    changelog.txt.
 | 
			
		||||
 | 
			
		||||
.. _changelog:
 | 
			
		||||
 | 
			
		||||
#########
 | 
			
		||||
Changelog
 | 
			
		||||
#########
 | 
			
		||||
 | 
			
		||||
.. contents::
 | 
			
		||||
   :depth: 2
 | 
			
		||||
 | 
			
		||||
.. include:: /docs/_auto/news.rst
 | 
			
		||||
@ -0,0 +1,46 @@
 | 
			
		||||
================================================================================
 | 
			
		||||
# Future
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
================================================================================
 | 
			
		||||
# 0.44.09-alpha1
 | 
			
		||||
 | 
			
		||||
## Fixes
 | 
			
		||||
- `digtype`: stopped designating non-vein tiles (open space, trees, etc.)
 | 
			
		||||
- `labormanager`: fixed crash due to dig jobs targeting some unrevealed map blocks
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
================================================================================
 | 
			
		||||
# 0.44.08-alpha1
 | 
			
		||||
 | 
			
		||||
## Fixes
 | 
			
		||||
- `fix/dead-units`: fixed a bug that could remove some arriving (not dead) units
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
================================================================================
 | 
			
		||||
# 0.44.07-beta1
 | 
			
		||||
 | 
			
		||||
## Structures
 | 
			
		||||
-@ Added symbols for Toady's `0.44.07 Linux test build <http://www.bay12forums.com/smf/index.php?topic=169839.msg7720111#msg7720111>`_ to fix :bug:`10615`
 | 
			
		||||
-@ ``world_site``: fixed alignment
 | 
			
		||||
 | 
			
		||||
## Misc improvements
 | 
			
		||||
- `modtools/item-trigger`: added the ability to specify inventory mode(s) to trigger on
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
================================================================================
 | 
			
		||||
# 0.44.07-alpha1
 | 
			
		||||
 | 
			
		||||
## Fixes
 | 
			
		||||
- `embark-assistant`: fixed detection of reanimating biomes
 | 
			
		||||
 | 
			
		||||
## Structures
 | 
			
		||||
- Several new names in instrument raw structures
 | 
			
		||||
- ``identity``: identified ``profession``, ``civ``
 | 
			
		||||
- ``manager_order_template``: fixed last field type
 | 
			
		||||
- ``viewscreen_createquotast``: fixed layout
 | 
			
		||||
- ``world.language``: moved ``colors``, ``shapes``, ``patterns`` to ``world.descriptors``
 | 
			
		||||
- ``world.reactions``, ``world.reaction_categories``:\ moved to new compound, ``world.reactions``. Requires renaming
 | 
			
		||||
 | 
			
		||||
    - ``world.reactions`` to ``world.reactions.reactions``
 | 
			
		||||
    - ``world.reaction_categories`` to ``world.reactions.reaction_categories``
 | 
			
		||||
@ -0,0 +1,177 @@
 | 
			
		||||
import collections
 | 
			
		||||
import copy
 | 
			
		||||
import itertools
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
CHANGELOG_SECTIONS = [
 | 
			
		||||
    'New Plugins',
 | 
			
		||||
    'New Scripts',
 | 
			
		||||
    'New Tweaks',
 | 
			
		||||
    'New Features',
 | 
			
		||||
    'New Internal Commands',
 | 
			
		||||
    'Fixes',
 | 
			
		||||
    'Misc Improvements',
 | 
			
		||||
    'Removed',
 | 
			
		||||
    'Internals',
 | 
			
		||||
    'Structures',
 | 
			
		||||
    'Lua',
 | 
			
		||||
    'Ruby',
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
class ChangelogEntry(object):
 | 
			
		||||
    def __init__(self, text, section, stable_version, dev_version):
 | 
			
		||||
        text = text.lstrip('- ')
 | 
			
		||||
        # normalize section to title case
 | 
			
		||||
        self.section = ' '.join(word[0].upper() + word[1:].lower()
 | 
			
		||||
                                for word in section.strip().split())
 | 
			
		||||
        self.stable_version = stable_version
 | 
			
		||||
        self.dev_version = dev_version
 | 
			
		||||
        self.dev_only = text.startswith('@')
 | 
			
		||||
        text = text.lstrip('@ ')
 | 
			
		||||
        self.children = []
 | 
			
		||||
 | 
			
		||||
        split_index = text.find(': ')
 | 
			
		||||
        if split_index != -1:
 | 
			
		||||
            self.feature, description = text[:split_index], text[split_index+1:]
 | 
			
		||||
            if description.strip():
 | 
			
		||||
                self.children.insert(0, description.strip())
 | 
			
		||||
        else:
 | 
			
		||||
            self.feature = text
 | 
			
		||||
        self.feature = self.feature.replace(':\\', ':')
 | 
			
		||||
 | 
			
		||||
        self.sort_key = self.feature.upper()
 | 
			
		||||
 | 
			
		||||
    def __repr__(self):
 | 
			
		||||
        return 'ChangelogEntry(%r, %r)' % (self.feature, self.children)
 | 
			
		||||
 | 
			
		||||
def parse_changelog():
 | 
			
		||||
    cur_stable = None
 | 
			
		||||
    cur_dev = None
 | 
			
		||||
    cur_section = None
 | 
			
		||||
    last_entry = None
 | 
			
		||||
    entries = []
 | 
			
		||||
 | 
			
		||||
    with open('docs/changelog.txt') as f:
 | 
			
		||||
        for line_id, line in enumerate(f.readlines()):
 | 
			
		||||
            line_id += 1
 | 
			
		||||
            if not line.strip() or line.startswith('==='):
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if line.startswith('##'):
 | 
			
		||||
                cur_section = line.lstrip('#').strip()
 | 
			
		||||
            elif line.startswith('#'):
 | 
			
		||||
                cur_dev = line.lstrip('#').strip().lower()
 | 
			
		||||
                if ('alpha' not in cur_dev and 'beta' not in cur_dev and
 | 
			
		||||
                        'rc' not in cur_dev):
 | 
			
		||||
                    cur_stable = cur_dev
 | 
			
		||||
            elif line.startswith('-'):
 | 
			
		||||
                if not cur_stable or not cur_dev or not cur_section:
 | 
			
		||||
                    raise ValueError(
 | 
			
		||||
                        'changelog.txt:%i: Entry without section' % line_id)
 | 
			
		||||
                last_entry = ChangelogEntry(line.strip(), cur_section,
 | 
			
		||||
                                            cur_stable, cur_dev)
 | 
			
		||||
                entries.append(last_entry)
 | 
			
		||||
                # entries.setdefault(cur_stable, []).append(last_entry)
 | 
			
		||||
                # entries.setdefault(cur_dev, []).append(last_entry)
 | 
			
		||||
            elif line.lstrip().startswith('-'):
 | 
			
		||||
                if not cur_stable or not cur_dev:
 | 
			
		||||
                    raise ValueError(
 | 
			
		||||
                        'changelog.txt:%i: Sub-entry without section' % line_id)
 | 
			
		||||
                if not last_entry:
 | 
			
		||||
                    raise ValueError(
 | 
			
		||||
                        'changelog.txt:%i: Sub-entry without parent' % line_id)
 | 
			
		||||
                last_entry.children.append(line.strip('- \n'))
 | 
			
		||||
            else:
 | 
			
		||||
                raise ValueError('Invalid line: ' + line)
 | 
			
		||||
 | 
			
		||||
    return entries
 | 
			
		||||
 | 
			
		||||
def consolidate_changelog(all_entries):
 | 
			
		||||
    for sections in all_entries.values():
 | 
			
		||||
        for section, entries in sections.items():
 | 
			
		||||
            # sort() is stable, so reverse entries so that older entries for the
 | 
			
		||||
            # same feature are on top
 | 
			
		||||
            entries.reverse()
 | 
			
		||||
            entries.sort(key=lambda entry: entry.sort_key)
 | 
			
		||||
            new_entries = []
 | 
			
		||||
            for feature, group in itertools.groupby(entries,
 | 
			
		||||
                                                    lambda e: e.feature):
 | 
			
		||||
                old_entries = list(group)
 | 
			
		||||
                children = list(itertools.chain(*[entry.children
 | 
			
		||||
                                for entry in old_entries]))
 | 
			
		||||
                new_entry = copy.deepcopy(old_entries[0])
 | 
			
		||||
                new_entry.children = children
 | 
			
		||||
                new_entries.append(new_entry)
 | 
			
		||||
            entries[:] = new_entries
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_changelog(versions, all_entries, path):
 | 
			
		||||
    # all_entries: version -> section -> entry
 | 
			
		||||
    with open(path, 'w') as f:
 | 
			
		||||
        write = lambda s: f.write(s + '\n')
 | 
			
		||||
        for version in versions:
 | 
			
		||||
            sections = all_entries[version]
 | 
			
		||||
            if not sections:
 | 
			
		||||
                continue
 | 
			
		||||
            version = 'DFHack ' + version
 | 
			
		||||
            write(version)
 | 
			
		||||
            write('=' * len(version))
 | 
			
		||||
            write('')
 | 
			
		||||
            for section in CHANGELOG_SECTIONS:
 | 
			
		||||
                entries = sections[section]
 | 
			
		||||
                if not entries:
 | 
			
		||||
                    continue
 | 
			
		||||
                write(section)
 | 
			
		||||
                write('-' * len(section))
 | 
			
		||||
                for entry in entries:
 | 
			
		||||
                    if len(entry.children) == 1:
 | 
			
		||||
                        write('- ' + entry.feature + ': ' +
 | 
			
		||||
                                entry.children[0].strip('- '))
 | 
			
		||||
                        continue
 | 
			
		||||
                    elif entry.children:
 | 
			
		||||
                        write('- ' + entry.feature + ':')
 | 
			
		||||
                        write('')
 | 
			
		||||
                        for child in entry.children:
 | 
			
		||||
                            write('    - ' + child)
 | 
			
		||||
                        write('')
 | 
			
		||||
                    else:
 | 
			
		||||
                        write('- ' + entry.feature)
 | 
			
		||||
                write('')
 | 
			
		||||
            write('')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def generate_changelog():
 | 
			
		||||
    entries = parse_changelog()
 | 
			
		||||
 | 
			
		||||
    # scan for unrecognized sections
 | 
			
		||||
    for entry in entries:
 | 
			
		||||
        if entry.section not in CHANGELOG_SECTIONS:
 | 
			
		||||
            raise RuntimeWarning('Unknown section: ' + entry.section)
 | 
			
		||||
 | 
			
		||||
    # ordered versions
 | 
			
		||||
    versions = ['future']
 | 
			
		||||
    # map versions to stable versions
 | 
			
		||||
    stable_version_map = {}
 | 
			
		||||
    # version -> section -> entry
 | 
			
		||||
    stable_entries = collections.defaultdict(lambda:
 | 
			
		||||
                        collections.defaultdict(list))
 | 
			
		||||
    dev_entries = collections.defaultdict(lambda:
 | 
			
		||||
                        collections.defaultdict(list))
 | 
			
		||||
    for entry in entries:
 | 
			
		||||
        if entry.dev_version not in versions:
 | 
			
		||||
            versions.append(entry.dev_version)
 | 
			
		||||
        stable_version_map.setdefault(entry.dev_version, entry.stable_version)
 | 
			
		||||
        if not entry.dev_only:
 | 
			
		||||
            stable_entries[entry.stable_version][entry.section].append(entry)
 | 
			
		||||
        dev_entries[entry.dev_version][entry.section].append(entry)
 | 
			
		||||
 | 
			
		||||
    consolidate_changelog(stable_entries)
 | 
			
		||||
 | 
			
		||||
    print_changelog(versions, stable_entries, 'docs/_auto/news.rst')
 | 
			
		||||
    print_changelog(versions, dev_entries, 'docs/_auto/news-dev.rst')
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    os.chdir(os.path.abspath(os.path.dirname(__file__)))
 | 
			
		||||
    os.chdir('..')
 | 
			
		||||
    generate_changelog()
 | 
			
		||||
		Loading…
	
		Reference in New Issue