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