From 708a9a9d8d7e44353edcf1f9742b193bd319e067 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Wed, 8 Jun 2016 15:30:31 -0400 Subject: [PATCH 01/19] Update headings --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 902599c39..964a196e7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Switch to an existing version control working directory, or create one: $ git init . ``` -## Create a document +## Create documents Create a new parent requirements document: @@ -82,7 +82,7 @@ $ doorstop add SRD $ doorstop add SRD ``` -## Links items +## Link items Create a child document to link to the parent: From 183845e9b615f16797fd3abdd1172a3e8dff0698 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Wed, 8 Jun 2016 15:48:08 -0400 Subject: [PATCH 02/19] Move the requirements to the root --- docs/reqs/assets/logo-black-white.png | 1 - doorstop/cli/test/__init__.py | 2 +- doorstop/cli/test/test_all.py | 2 +- {docs/reqs => reqs}/.doorstop.yml | 0 {docs/reqs => reqs}/REQ001.yml | 0 {docs/reqs => reqs}/REQ002.yml | 0 {docs/reqs => reqs}/REQ003.yml | 0 {docs/reqs => reqs}/REQ004.yml | 0 {docs/reqs => reqs}/REQ006.yml | 0 {docs/reqs => reqs}/REQ007.yml | 0 {docs/reqs => reqs}/REQ008.yml | 0 {docs/reqs => reqs}/REQ009.yml | 0 {docs/reqs => reqs}/REQ010.yml | 0 {docs/reqs => reqs}/REQ011.yml | 0 {docs/reqs => reqs}/REQ012.yml | 0 {docs/reqs => reqs}/REQ013.yml | 0 {docs/reqs => reqs}/REQ014.yml | 0 {docs/reqs => reqs}/REQ015.yml | 0 {docs/reqs => reqs}/REQ016.yml | 0 {docs/reqs => reqs}/REQ017.yml | 0 {docs/reqs => reqs}/REQ018.yml | 0 {docs/reqs => reqs}/REQ019.yml | 0 reqs/assets/logo-black-white.png | 1 + {docs/reqs => reqs}/tutorial/.doorstop.yml | 0 {docs/reqs => reqs}/tutorial/TUT001.yml | 0 {docs/reqs => reqs}/tutorial/TUT002.yml | 0 {docs/reqs => reqs}/tutorial/TUT003.yml | 0 {docs/reqs => reqs}/tutorial/TUT004.yml | 0 {docs/reqs => reqs}/tutorial/TUT005.yml | 0 {docs/reqs => reqs}/tutorial/TUT008.yml | 0 {docs/reqs => reqs}/tutorial/TUT009.yml | 0 {docs/reqs => reqs}/tutorial/TUT010.yml | 0 {docs/reqs => reqs}/tutorial/TUT011.yml | 0 {docs/reqs => reqs}/tutorial/TUT012.yml | 0 {docs/reqs => reqs}/tutorial/TUT013.yml | 0 {docs/reqs => reqs}/tutorial/TUT014.yml | 0 {docs/reqs => reqs}/tutorial/TUT015.yml | 0 {docs/reqs => reqs}/tutorial/TUT016.yml | 0 38 files changed, 3 insertions(+), 3 deletions(-) delete mode 120000 docs/reqs/assets/logo-black-white.png rename {docs/reqs => reqs}/.doorstop.yml (100%) rename {docs/reqs => reqs}/REQ001.yml (100%) rename {docs/reqs => reqs}/REQ002.yml (100%) rename {docs/reqs => reqs}/REQ003.yml (100%) rename {docs/reqs => reqs}/REQ004.yml (100%) rename {docs/reqs => reqs}/REQ006.yml (100%) rename {docs/reqs => reqs}/REQ007.yml (100%) rename {docs/reqs => reqs}/REQ008.yml (100%) rename {docs/reqs => reqs}/REQ009.yml (100%) rename {docs/reqs => reqs}/REQ010.yml (100%) rename {docs/reqs => reqs}/REQ011.yml (100%) rename {docs/reqs => reqs}/REQ012.yml (100%) rename {docs/reqs => reqs}/REQ013.yml (100%) rename {docs/reqs => reqs}/REQ014.yml (100%) rename {docs/reqs => reqs}/REQ015.yml (100%) rename {docs/reqs => reqs}/REQ016.yml (100%) rename {docs/reqs => reqs}/REQ017.yml (100%) rename {docs/reqs => reqs}/REQ018.yml (100%) rename {docs/reqs => reqs}/REQ019.yml (100%) create mode 120000 reqs/assets/logo-black-white.png rename {docs/reqs => reqs}/tutorial/.doorstop.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT001.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT002.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT003.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT004.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT005.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT008.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT009.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT010.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT011.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT012.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT013.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT014.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT015.yml (100%) rename {docs/reqs => reqs}/tutorial/TUT016.yml (100%) diff --git a/docs/reqs/assets/logo-black-white.png b/docs/reqs/assets/logo-black-white.png deleted file mode 120000 index d150038e8..000000000 --- a/docs/reqs/assets/logo-black-white.png +++ /dev/null @@ -1 +0,0 @@ -../../images/logo-black-white.png \ No newline at end of file diff --git a/doorstop/cli/test/__init__.py b/doorstop/cli/test/__init__.py index dceef8c55..06a19ae48 100644 --- a/doorstop/cli/test/__init__.py +++ b/doorstop/cli/test/__init__.py @@ -7,7 +7,7 @@ from doorstop import settings ROOT = os.path.join(os.path.dirname(__file__), '..', '..', '..') -REQS = os.path.join(ROOT, 'docs', 'reqs') +REQS = os.path.join(ROOT, 'reqs') TUTORIAL = os.path.join(REQS, 'tutorial') FILES = os.path.join(os.path.dirname(__file__), 'files') diff --git a/doorstop/cli/test/test_all.py b/doorstop/cli/test/test_all.py index 5fafd7dde..fe12d9d18 100644 --- a/doorstop/cli/test/test_all.py +++ b/doorstop/cli/test/test_all.py @@ -230,7 +230,7 @@ class TestReorder(unittest.TestCase): @classmethod def setUpClass(cls): cls.prefix = 'tut' - cls.path = os.path.join('docs', 'reqs', 'tutorial', 'index.yml') + cls.path = os.path.join('reqs', 'tutorial', 'index.yml') def tearDown(self): common.delete(self.path) diff --git a/docs/reqs/.doorstop.yml b/reqs/.doorstop.yml similarity index 100% rename from docs/reqs/.doorstop.yml rename to reqs/.doorstop.yml diff --git a/docs/reqs/REQ001.yml b/reqs/REQ001.yml similarity index 100% rename from docs/reqs/REQ001.yml rename to reqs/REQ001.yml diff --git a/docs/reqs/REQ002.yml b/reqs/REQ002.yml similarity index 100% rename from docs/reqs/REQ002.yml rename to reqs/REQ002.yml diff --git a/docs/reqs/REQ003.yml b/reqs/REQ003.yml similarity index 100% rename from docs/reqs/REQ003.yml rename to reqs/REQ003.yml diff --git a/docs/reqs/REQ004.yml b/reqs/REQ004.yml similarity index 100% rename from docs/reqs/REQ004.yml rename to reqs/REQ004.yml diff --git a/docs/reqs/REQ006.yml b/reqs/REQ006.yml similarity index 100% rename from docs/reqs/REQ006.yml rename to reqs/REQ006.yml diff --git a/docs/reqs/REQ007.yml b/reqs/REQ007.yml similarity index 100% rename from docs/reqs/REQ007.yml rename to reqs/REQ007.yml diff --git a/docs/reqs/REQ008.yml b/reqs/REQ008.yml similarity index 100% rename from docs/reqs/REQ008.yml rename to reqs/REQ008.yml diff --git a/docs/reqs/REQ009.yml b/reqs/REQ009.yml similarity index 100% rename from docs/reqs/REQ009.yml rename to reqs/REQ009.yml diff --git a/docs/reqs/REQ010.yml b/reqs/REQ010.yml similarity index 100% rename from docs/reqs/REQ010.yml rename to reqs/REQ010.yml diff --git a/docs/reqs/REQ011.yml b/reqs/REQ011.yml similarity index 100% rename from docs/reqs/REQ011.yml rename to reqs/REQ011.yml diff --git a/docs/reqs/REQ012.yml b/reqs/REQ012.yml similarity index 100% rename from docs/reqs/REQ012.yml rename to reqs/REQ012.yml diff --git a/docs/reqs/REQ013.yml b/reqs/REQ013.yml similarity index 100% rename from docs/reqs/REQ013.yml rename to reqs/REQ013.yml diff --git a/docs/reqs/REQ014.yml b/reqs/REQ014.yml similarity index 100% rename from docs/reqs/REQ014.yml rename to reqs/REQ014.yml diff --git a/docs/reqs/REQ015.yml b/reqs/REQ015.yml similarity index 100% rename from docs/reqs/REQ015.yml rename to reqs/REQ015.yml diff --git a/docs/reqs/REQ016.yml b/reqs/REQ016.yml similarity index 100% rename from docs/reqs/REQ016.yml rename to reqs/REQ016.yml diff --git a/docs/reqs/REQ017.yml b/reqs/REQ017.yml similarity index 100% rename from docs/reqs/REQ017.yml rename to reqs/REQ017.yml diff --git a/docs/reqs/REQ018.yml b/reqs/REQ018.yml similarity index 100% rename from docs/reqs/REQ018.yml rename to reqs/REQ018.yml diff --git a/docs/reqs/REQ019.yml b/reqs/REQ019.yml similarity index 100% rename from docs/reqs/REQ019.yml rename to reqs/REQ019.yml diff --git a/reqs/assets/logo-black-white.png b/reqs/assets/logo-black-white.png new file mode 120000 index 000000000..987515a82 --- /dev/null +++ b/reqs/assets/logo-black-white.png @@ -0,0 +1 @@ +../../docs/images/logo-black-white.png \ No newline at end of file diff --git a/docs/reqs/tutorial/.doorstop.yml b/reqs/tutorial/.doorstop.yml similarity index 100% rename from docs/reqs/tutorial/.doorstop.yml rename to reqs/tutorial/.doorstop.yml diff --git a/docs/reqs/tutorial/TUT001.yml b/reqs/tutorial/TUT001.yml similarity index 100% rename from docs/reqs/tutorial/TUT001.yml rename to reqs/tutorial/TUT001.yml diff --git a/docs/reqs/tutorial/TUT002.yml b/reqs/tutorial/TUT002.yml similarity index 100% rename from docs/reqs/tutorial/TUT002.yml rename to reqs/tutorial/TUT002.yml diff --git a/docs/reqs/tutorial/TUT003.yml b/reqs/tutorial/TUT003.yml similarity index 100% rename from docs/reqs/tutorial/TUT003.yml rename to reqs/tutorial/TUT003.yml diff --git a/docs/reqs/tutorial/TUT004.yml b/reqs/tutorial/TUT004.yml similarity index 100% rename from docs/reqs/tutorial/TUT004.yml rename to reqs/tutorial/TUT004.yml diff --git a/docs/reqs/tutorial/TUT005.yml b/reqs/tutorial/TUT005.yml similarity index 100% rename from docs/reqs/tutorial/TUT005.yml rename to reqs/tutorial/TUT005.yml diff --git a/docs/reqs/tutorial/TUT008.yml b/reqs/tutorial/TUT008.yml similarity index 100% rename from docs/reqs/tutorial/TUT008.yml rename to reqs/tutorial/TUT008.yml diff --git a/docs/reqs/tutorial/TUT009.yml b/reqs/tutorial/TUT009.yml similarity index 100% rename from docs/reqs/tutorial/TUT009.yml rename to reqs/tutorial/TUT009.yml diff --git a/docs/reqs/tutorial/TUT010.yml b/reqs/tutorial/TUT010.yml similarity index 100% rename from docs/reqs/tutorial/TUT010.yml rename to reqs/tutorial/TUT010.yml diff --git a/docs/reqs/tutorial/TUT011.yml b/reqs/tutorial/TUT011.yml similarity index 100% rename from docs/reqs/tutorial/TUT011.yml rename to reqs/tutorial/TUT011.yml diff --git a/docs/reqs/tutorial/TUT012.yml b/reqs/tutorial/TUT012.yml similarity index 100% rename from docs/reqs/tutorial/TUT012.yml rename to reqs/tutorial/TUT012.yml diff --git a/docs/reqs/tutorial/TUT013.yml b/reqs/tutorial/TUT013.yml similarity index 100% rename from docs/reqs/tutorial/TUT013.yml rename to reqs/tutorial/TUT013.yml diff --git a/docs/reqs/tutorial/TUT014.yml b/reqs/tutorial/TUT014.yml similarity index 100% rename from docs/reqs/tutorial/TUT014.yml rename to reqs/tutorial/TUT014.yml diff --git a/docs/reqs/tutorial/TUT015.yml b/reqs/tutorial/TUT015.yml similarity index 100% rename from docs/reqs/tutorial/TUT015.yml rename to reqs/tutorial/TUT015.yml diff --git a/docs/reqs/tutorial/TUT016.yml b/reqs/tutorial/TUT016.yml similarity index 100% rename from docs/reqs/tutorial/TUT016.yml rename to reqs/tutorial/TUT016.yml From 9805fa9dd0e07aae13ed20664680064afc884bb1 Mon Sep 17 00:00:00 2001 From: Michael Thompson Date: Thu, 16 Jun 2016 17:30:43 +0100 Subject: [PATCH 03/19] Report the item id that generated the exception --- doorstop/core/document.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doorstop/core/document.py b/doorstop/core/document.py index 51fa9044b..7df19ad09 100644 --- a/doorstop/core/document.py +++ b/doorstop/core/document.py @@ -193,7 +193,11 @@ def _iter(self, reload=False): else: self._items.append(item) if reload: - item.load(reload=reload) + try: + item.load(reload=reload) + except Exception as e: + print("!! Exception loading %s" % item) + raise e if settings.CACHE_ITEMS and self.tree: self.tree._item_cache[item.uid] = item # pylint: disable=W0212 log.trace("cached item: {}".format(item)) From b95ffdf75003297f2c5e7d9d0d7f17cf833f8887 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 17 Jun 2016 16:06:34 +0100 Subject: [PATCH 04/19] Return an empty list of ignores even when the property svn:ignore isn't set --- doorstop/core/vcs/subversion.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doorstop/core/vcs/subversion.py b/doorstop/core/vcs/subversion.py index 00dd8ef2e..0314201ae 100644 --- a/doorstop/core/vcs/subversion.py +++ b/doorstop/core/vcs/subversion.py @@ -33,7 +33,8 @@ def commit(self, message=None): @property def ignores(self): # pragma: no cover (manual test) - if not self._ignores_cache: + if self._ignores_cache is None: + self._ignores_cache = [] os.chdir(self.path) for line in self.call('svn', 'pg', '-R', 'svn:ignore', '.', return_stdout=True).splitlines(): From c7aecce692b7a878d579ad364cfce7d9bf02a068 Mon Sep 17 00:00:00 2001 From: Michael Thompson Date: Mon, 20 Jun 2016 09:46:10 +0100 Subject: [PATCH 05/19] print an error message containing the UID when an item cannot be loaded --- doorstop/core/document.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doorstop/core/document.py b/doorstop/core/document.py index 7df19ad09..31b93c7ae 100644 --- a/doorstop/core/document.py +++ b/doorstop/core/document.py @@ -195,9 +195,9 @@ def _iter(self, reload=False): if reload: try: item.load(reload=reload) - except Exception as e: - print("!! Exception loading %s" % item) - raise e + except Exception: + log.error("Unable to load: %s", item) + raise if settings.CACHE_ITEMS and self.tree: self.tree._item_cache[item.uid] = item # pylint: disable=W0212 log.trace("cached item: {}".format(item)) From 3f6ebb6bb86f4788f3ba15079474866a4f35a497 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Wed, 10 Aug 2016 12:38:49 -0400 Subject: [PATCH 06/19] Optimize README for ReadTheDocs --- README.md | 15 ++++++++------- mkdocs.yml | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 964a196e7..72e645c41 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,19 @@ -**STATUS:** As of the 1.0 release, this project is no longer under active development. I still plan on accepting any passing pull requests to become part of future 2.x releases. +**STATUS:** As of the 1.0 release, this project is no longer under active development. Passing pull requests will be considered for future 1.x and 2.x releases. ----- -# Doorstop - [![Build Status](http://img.shields.io/travis/jacebrowning/doorstop/master.svg)](https://travis-ci.org/jacebrowning/doorstop) [![Coverage Status](http://img.shields.io/coveralls/jacebrowning/doorstop/master.svg)](https://coveralls.io/r/jacebrowning/doorstop) [![Scrutinizer Code Quality](http://img.shields.io/scrutinizer/g/jacebrowning/doorstop.svg)](https://scrutinizer-ci.com/g/jacebrowning/doorstop/?branch=master) [![PyPI Version](http://img.shields.io/pypi/v/Doorstop.svg)](https://pypi.python.org/pypi/Doorstop) [![PyPI Downloads](http://img.shields.io/pypi/dm/Doorstop.svg)](https://pypi.python.org/pypi/Doorstop) +# Overview + Doorstop manages the storage of textual requirements alongside source code in version control. - + + When a project utilizes this tool, each linkable item (requirement, test case, etc.) is stored as a YAML file in a designated directory. The items in each directory form a document. The relationship between documents forms a tree hierarchy. Doorstop provides mechanisms for modifying this tree, validating item traceability, and publishing documents in several formats. Additional reading: @@ -21,7 +22,7 @@ Additional reading: - talks: [GRDevDay](https://speakerdeck.com/jacebrowning/doorstop-requirements-management-using-python-and-version-control), [BarCamp](https://speakerdeck.com/jacebrowning/strip-searched-a-rough-introduction-to-requirements-management) - sample: [Generated HTML](http://jacebrowning.github.io/doorstop/index.html) -# Getting Started +# Setup ## Requirements @@ -30,7 +31,7 @@ Additional reading: ## Installation -Doorstop can be installed with pip: +Install Doorstop with pip: ``` $ pip install doorstop @@ -58,7 +59,7 @@ $ python >>> doorstop.__version__ ``` -# Basic Usage +# Usage Switch to an existing version control working directory, or create one: diff --git a/mkdocs.yml b/mkdocs.yml index 38a2646f1..93385cbba 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,6 +15,6 @@ pages: - Graphical Interface: gui/coming-soon.md - Scripting Interface: api/scripting.md - About: - - 'Release Notes': about/changelog.md + - Release Notes: about/changelog.md - Contributing: about/contributing.md - License: about/license.md From 75f831b1a07801e7b6fd595bda9c6ae04a94fa3a Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 13:42:53 +0000 Subject: [PATCH 07/19] Warn if an item doesn't have a link from every child document --- doorstop/core/document.py | 4 ++-- doorstop/core/item.py | 14 +++++++++++--- doorstop/core/tree.py | 10 +++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/doorstop/core/document.py b/doorstop/core/document.py index 51fa9044b..27957fc51 100644 --- a/doorstop/core/document.py +++ b/doorstop/core/document.py @@ -587,7 +587,7 @@ def find_item(self, value, _kind=''): raise DoorstopError("no matching{} UID: {}".format(_kind, uid)) - def get_issues(self, skip=None, item_hook=None, **kwargs): + def get_issues(self, children, skip=None, item_hook=None, **kwargs): """Yield all the document's issues. :param skip: list of document prefixes to skip @@ -625,7 +625,7 @@ def get_issues(self, skip=None, item_hook=None, **kwargs): # Check item for issue in chain(hook(item=item, document=self, tree=self.tree), - item.get_issues(skip=skip)): + item.get_issues(children, skip=skip)): # Prepend the item's UID to yielded exceptions if isinstance(issue, Exception): diff --git a/doorstop/core/item.py b/doorstop/core/item.py index da85fd7aa..92f50055f 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -507,7 +507,7 @@ def unlink(self, value): except KeyError: log.warning("link to {0} does not exist".format(uid)) - def get_issues(self, skip=None, **kwargs): + def get_issues(self, children, skip=None, **kwargs): """Yield all the item's issues. :param skip: list of document prefixes to skip @@ -560,7 +560,7 @@ def get_issues(self, skip=None, **kwargs): # Check links against both document and tree if self.document and self.tree: - yield from self._get_issues_both(self.document, self.tree, skip) + yield from self._get_issues_both(self.document, self.tree, skip, children) # Check review status if not self.reviewed: @@ -652,7 +652,7 @@ def _get_issues_tree(self, tree): if settings.REFORMAT: self._data['links'] = identifiers - def _get_issues_both(self, document, tree, skip): + def _get_issues_both(self, document, tree, skip, children): """Yield all the item's issues against its document and tree.""" log.debug("getting issues against document and tree...") @@ -673,6 +673,14 @@ def _get_issues_both(self, document, tree, skip): continue msg = "no links from child document: {}".format(document) yield DoorstopWarning(msg) + else: + prefix = [item.prefix for item in items] + for child in children: + if child in skip: + continue + if child not in prefix: + yield DoorstopWarning('no links from document: {}'.format(child)) + @requires_tree def find_ref(self): diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index 950c546e9..c86a79534 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -152,7 +152,7 @@ def _place(self, document): # Current document is the parent node = Tree(document, self) self.children.append(node) - + else: # Search for the parent @@ -437,9 +437,13 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): if not documents: yield DoorstopWarning("no documents") # Check each document - for document in documents: + trees = [self] + trees.extend(self.children) + for branch in trees: + document = branch.document for issue in chain(hook(document=document, tree=self), - document.get_issues(skip=skip, + document.get_issues(children=[child.document.prefix for child in branch.children], + skip=skip, item_hook=item_hook)): # Prepend the document's prefix to yielded exceptions if isinstance(issue, Exception): From 61cada80c464ab1d2057add71dd0a6aa0807bbc9 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 17:00:02 +0000 Subject: [PATCH 08/19] find_all needs to be true, fix line wrapping --- doorstop/core/item.py | 9 ++++++--- doorstop/core/tree.py | 5 +++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/doorstop/core/item.py b/doorstop/core/item.py index 92f50055f..7412c6a20 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -560,7 +560,8 @@ def get_issues(self, children, skip=None, **kwargs): # Check links against both document and tree if self.document and self.tree: - yield from self._get_issues_both(self.document, self.tree, skip, children) + yield from self._get_issues_both(self.document, self.tree, + skip, children) # Check review status if not self.reviewed: @@ -664,7 +665,8 @@ def _get_issues_both(self, document, tree, skip, children): if settings.CHECK_CHILD_LINKS and self.normative: items, documents = self._find_child_objects(document=document, tree=tree, - find_all=False) + find_all=True) + if not items: for document in documents: if document.prefix in skip: @@ -679,7 +681,8 @@ def _get_issues_both(self, document, tree, skip, children): if child in skip: continue if child not in prefix: - yield DoorstopWarning('no links from document: {}'.format(child)) + msg = 'no links from document: {}'.format(child) + yield DoorstopWarning(msg) @requires_tree diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index c86a79534..a50a25016 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -438,11 +438,12 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): yield DoorstopWarning("no documents") # Check each document trees = [self] - trees.extend(self.children) + #trees.extend(self.children) for branch in trees: document = branch.document for issue in chain(hook(document=document, tree=self), - document.get_issues(children=[child.document.prefix for child in branch.children], + document.get_issues(children=[child.document.prefix for child \ + in branch.children], skip=skip, item_hook=item_hook)): # Prepend the document's prefix to yielded exceptions From 59b63642a546faf1e8667d0206b5fbc403c602b5 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 17:16:24 +0000 Subject: [PATCH 09/19] Add command line argument --- doorstop/cli/main.py | 2 ++ doorstop/cli/utilities.py | 2 ++ doorstop/core/item.py | 2 +- doorstop/settings.py | 1 + 4 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doorstop/cli/main.py b/doorstop/cli/main.py index 5f19e6fa9..571cf7226 100644 --- a/doorstop/cli/main.py +++ b/doorstop/cli/main.py @@ -52,6 +52,8 @@ def main(args=None): # pylint: disable=R0915 help="do not validate external file references") parser.add_argument('-C', '--no-child-check', action='store_true', help="do not validate child (reverse) links") + parser.add_argument('-Z', '--strict-child-check', action='store_true', + help="Strictly validate (reverse) links from every child document") parser.add_argument('-S', '--no-suspect-check', action='store_true', help="do not check for suspect links") parser.add_argument('-W', '--no-review-check', action='store_true', diff --git a/doorstop/cli/utilities.py b/doorstop/cli/utilities.py index 327fad0b1..0de61cc8d 100644 --- a/doorstop/cli/utilities.py +++ b/doorstop/cli/utilities.py @@ -90,6 +90,8 @@ def configure_settings(args): settings.CHECK_REF = args.no_ref_check is False if args.no_child_check is not None: settings.CHECK_CHILD_LINKS = args.no_child_check is False + if args.strict_child_check is not None: + settings.CHECK_CHILD_LINKS_STRICT = args.strict_child_check is True if args.no_suspect_check is not None: settings.CHECK_SUSPECT_LINKS = args.no_suspect_check is False if args.no_review_check is not None: diff --git a/doorstop/core/item.py b/doorstop/core/item.py index 7412c6a20..9c1b7e777 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -675,7 +675,7 @@ def _get_issues_both(self, document, tree, skip, children): continue msg = "no links from child document: {}".format(document) yield DoorstopWarning(msg) - else: + elif settings.CHECK_CHILD_LINKS_STRICT: prefix = [item.prefix for item in items] for child in children: if child in skip: diff --git a/doorstop/settings.py b/doorstop/settings.py index 1b242b5a2..de2368592 100644 --- a/doorstop/settings.py +++ b/doorstop/settings.py @@ -30,6 +30,7 @@ CHECK_LEVELS = True # validate document levels during validation CHECK_REF = True # validate external file references CHECK_CHILD_LINKS = True # validate reverse links +CHECK_CHILD_LINKS_STRICT = False # Strictly validate reverse links from all the child documents CHECK_SUSPECT_LINKS = True # check stamps on links CHECK_REVIEW_STATUS = True # check stamps on items WARN_ALL = False # display info-level issues as warnings From 95e8f198ad48dd351f5f2ff07d586febb42ba784 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 19:27:52 +0000 Subject: [PATCH 10/19] pep8 fixes --- doorstop/core/item.py | 5 ++--- doorstop/core/tree.py | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/doorstop/core/item.py b/doorstop/core/item.py index 9c1b7e777..fce1c50ed 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -560,7 +560,7 @@ def get_issues(self, children, skip=None, **kwargs): # Check links against both document and tree if self.document and self.tree: - yield from self._get_issues_both(self.document, self.tree, + yield from self._get_issues_both(self.document, self.tree, skip, children) # Check review status @@ -666,7 +666,7 @@ def _get_issues_both(self, document, tree, skip, children): items, documents = self._find_child_objects(document=document, tree=tree, find_all=True) - + if not items: for document in documents: if document.prefix in skip: @@ -683,7 +683,6 @@ def _get_issues_both(self, document, tree, skip, children): if child not in prefix: msg = 'no links from document: {}'.format(child) yield DoorstopWarning(msg) - @requires_tree def find_ref(self): diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index a50a25016..66f0fd27a 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -152,7 +152,7 @@ def _place(self, document): # Current document is the parent node = Tree(document, self) self.children.append(node) - + else: # Search for the parent @@ -438,11 +438,10 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): yield DoorstopWarning("no documents") # Check each document trees = [self] - #trees.extend(self.children) for branch in trees: document = branch.document for issue in chain(hook(document=document, tree=self), - document.get_issues(children=[child.document.prefix for child \ + document.get_issues(children=[child.document.prefix for child in branch.children], skip=skip, item_hook=item_hook)): From a9a7debf0f79cfe53cb37bd76384853eb4688812 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 20:40:45 +0000 Subject: [PATCH 11/19] require reverse links from every child document --- doorstop/cli/main.py | 2 +- doorstop/settings.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doorstop/cli/main.py b/doorstop/cli/main.py index 571cf7226..93edd4640 100644 --- a/doorstop/cli/main.py +++ b/doorstop/cli/main.py @@ -53,7 +53,7 @@ def main(args=None): # pylint: disable=R0915 parser.add_argument('-C', '--no-child-check', action='store_true', help="do not validate child (reverse) links") parser.add_argument('-Z', '--strict-child-check', action='store_true', - help="Strictly validate (reverse) links from every child document") + help="require child (reverse) links from every document") parser.add_argument('-S', '--no-suspect-check', action='store_true', help="do not check for suspect links") parser.add_argument('-W', '--no-review-check', action='store_true', diff --git a/doorstop/settings.py b/doorstop/settings.py index de2368592..5e6312aa7 100644 --- a/doorstop/settings.py +++ b/doorstop/settings.py @@ -30,7 +30,7 @@ CHECK_LEVELS = True # validate document levels during validation CHECK_REF = True # validate external file references CHECK_CHILD_LINKS = True # validate reverse links -CHECK_CHILD_LINKS_STRICT = False # Strictly validate reverse links from all the child documents +CHECK_CHILD_LINKS_STRICT = False # require child (reverse) links from every document CHECK_SUSPECT_LINKS = True # check stamps on links CHECK_REVIEW_STATUS = True # check stamps on items WARN_ALL = False # display info-level issues as warnings From 6babb9f36d39bdfa25276058c1ba379cbf564ea1 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 20:42:32 +0000 Subject: [PATCH 12/19] The tree can now return a list of child prefixes beneath the specified document --- doorstop/core/document.py | 10 ++++++++-- doorstop/core/item.py | 8 ++++---- doorstop/core/tree.py | 16 +++++++++++++--- 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/doorstop/core/document.py b/doorstop/core/document.py index 27957fc51..7ab303380 100644 --- a/doorstop/core/document.py +++ b/doorstop/core/document.py @@ -316,6 +316,12 @@ def index(self): if os.path.isfile(path): return path + @property + def children(self): + """Get the prefix of child documents that link to this document""" + children = self.tree.get_prefix_of_children(self) + return children + @index.setter def index(self, value): """Create or update the document's index.""" @@ -587,7 +593,7 @@ def find_item(self, value, _kind=''): raise DoorstopError("no matching{} UID: {}".format(_kind, uid)) - def get_issues(self, children, skip=None, item_hook=None, **kwargs): + def get_issues(self, skip=None, item_hook=None, **kwargs): """Yield all the document's issues. :param skip: list of document prefixes to skip @@ -625,7 +631,7 @@ def get_issues(self, children, skip=None, item_hook=None, **kwargs): # Check item for issue in chain(hook(item=item, document=self, tree=self.tree), - item.get_issues(children, skip=skip)): + item.get_issues(skip=skip)): # Prepend the item's UID to yielded exceptions if isinstance(issue, Exception): diff --git a/doorstop/core/item.py b/doorstop/core/item.py index fce1c50ed..5f3e1ccac 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -507,7 +507,7 @@ def unlink(self, value): except KeyError: log.warning("link to {0} does not exist".format(uid)) - def get_issues(self, children, skip=None, **kwargs): + def get_issues(self, skip=None, **kwargs): """Yield all the item's issues. :param skip: list of document prefixes to skip @@ -561,7 +561,7 @@ def get_issues(self, children, skip=None, **kwargs): # Check links against both document and tree if self.document and self.tree: yield from self._get_issues_both(self.document, self.tree, - skip, children) + skip) # Check review status if not self.reviewed: @@ -653,7 +653,7 @@ def _get_issues_tree(self, tree): if settings.REFORMAT: self._data['links'] = identifiers - def _get_issues_both(self, document, tree, skip, children): + def _get_issues_both(self, document, tree, skip): """Yield all the item's issues against its document and tree.""" log.debug("getting issues against document and tree...") @@ -677,7 +677,7 @@ def _get_issues_both(self, document, tree, skip, children): yield DoorstopWarning(msg) elif settings.CHECK_CHILD_LINKS_STRICT: prefix = [item.prefix for item in items] - for child in children: + for child in document.children: if child in skip: continue if child not in prefix: diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index 66f0fd27a..27f654b6a 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -151,6 +151,7 @@ def _place(self, document): # Current document is the parent node = Tree(document, self) + log.info("Set document %s.tree to %s", document, node) self.children.append(node) else: @@ -438,12 +439,11 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): yield DoorstopWarning("no documents") # Check each document trees = [self] + trees.extend(self.children) for branch in trees: document = branch.document for issue in chain(hook(document=document, tree=self), - document.get_issues(children=[child.document.prefix for child - in branch.children], - skip=skip, + document.get_issues(skip=skip, item_hook=item_hook)): # Prepend the document's prefix to yielded exceptions if isinstance(issue, Exception): @@ -481,6 +481,16 @@ def by_uid(row): # Sort rows return sorted(rows, key=by_uid) + def get_prefix_of_children(self, document): + """Return the prefixes of the children of this document + """ + for child in self.children: + if child.document == document: + children = [c.document.prefix for c in child.children] + return children + children = [c.document.prefix for c in self.children] + return children + def _iter_rows(self, item, mapping, parent=True, child=True, row=None): # pylint: disable=R0913 """Generate all traceability row slices. From 290d64a0c9361732744b6abb803ee2e825c330b3 Mon Sep 17 00:00:00 2001 From: michaelnt Date: Fri, 9 Dec 2016 20:48:33 +0000 Subject: [PATCH 13/19] Allow an empty tree to be validated --- doorstop/core/tree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index 27f654b6a..ac12f3353 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -442,6 +442,8 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): trees.extend(self.children) for branch in trees: document = branch.document + if not document: + continue for issue in chain(hook(document=document, tree=self), document.get_issues(skip=skip, item_hook=item_hook)): From 72a9b4d44277a98c8b84ed3d2d03ab79243da5dd Mon Sep 17 00:00:00 2001 From: Michael Thompson Date: Sun, 11 Dec 2016 22:00:34 +0000 Subject: [PATCH 14/19] Only update the document children when the tree is modified --- doorstop/core/document.py | 7 +------ doorstop/core/tree.py | 18 +++++++----------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/doorstop/core/document.py b/doorstop/core/document.py index 7ab303380..f6d9c5f7c 100644 --- a/doorstop/core/document.py +++ b/doorstop/core/document.py @@ -53,6 +53,7 @@ def __init__(self, path, root=os.getcwd(), **kwargs): self._data['parent'] = None # the root document does not have a parent self._items = [] self._itered = False + self.children = [] def __repr__(self): return "Document('{}')".format(self.path) @@ -316,12 +317,6 @@ def index(self): if os.path.isfile(path): return path - @property - def children(self): - """Get the prefix of child documents that link to this document""" - children = self.tree.get_prefix_of_children(self) - return children - @index.setter def index(self, value): """Create or update the document's index.""" diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index ac12f3353..bcada6a14 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -151,7 +151,6 @@ def _place(self, document): # Current document is the parent node = Tree(document, self) - log.info("Set document %s.tree to %s", document, node) self.children.append(node) else: @@ -177,6 +176,8 @@ def _place(self, document): log.info("parent options: {}".format(prefixes)) raise DoorstopError(msg) + for document in self: + self.get_prefix_of_children(document) # attributes ############################################################# @property @@ -438,12 +439,7 @@ def get_issues(self, skip=None, document_hook=None, item_hook=None): if not documents: yield DoorstopWarning("no documents") # Check each document - trees = [self] - trees.extend(self.children) - for branch in trees: - document = branch.document - if not document: - continue + for document in documents: for issue in chain(hook(document=document, tree=self), document.get_issues(skip=skip, item_hook=item_hook)): @@ -484,14 +480,14 @@ def by_uid(row): return sorted(rows, key=by_uid) def get_prefix_of_children(self, document): - """Return the prefixes of the children of this document - """ + """Return the prefixes of the children of this document.""" for child in self.children: if child.document == document: children = [c.document.prefix for c in child.children] - return children + document.children = children + return children = [c.document.prefix for c in self.children] - return children + document.children = children def _iter_rows(self, item, mapping, parent=True, child=True, row=None): # pylint: disable=R0913 """Generate all traceability row slices. From 807b75521930ce636d91a09617d223faf2970674 Mon Sep 17 00:00:00 2001 From: Michael Thompson Date: Sun, 11 Dec 2016 22:08:55 +0000 Subject: [PATCH 15/19] Only find all items when needed --- doorstop/core/item.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doorstop/core/item.py b/doorstop/core/item.py index 5f3e1ccac..392494911 100644 --- a/doorstop/core/item.py +++ b/doorstop/core/item.py @@ -663,9 +663,10 @@ def _get_issues_both(self, document, tree, skip): # Verify an item is being linked to (child links) if settings.CHECK_CHILD_LINKS and self.normative: + find_all = settings.CHECK_CHILD_LINKS_STRICT or False items, documents = self._find_child_objects(document=document, tree=tree, - find_all=True) + find_all=find_all) if not items: for document in documents: From 69503d22b8049c404ddd9c6999c8fa096009bb7a Mon Sep 17 00:00:00 2001 From: Michael Thompson Date: Mon, 12 Dec 2016 15:14:12 +0000 Subject: [PATCH 16/19] get_prefixes_of_children is now private and returns the children of a document --- doorstop/core/tree.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doorstop/core/tree.py b/doorstop/core/tree.py index bcada6a14..1bdc7f00b 100644 --- a/doorstop/core/tree.py +++ b/doorstop/core/tree.py @@ -177,7 +177,8 @@ def _place(self, document): raise DoorstopError(msg) for document in self: - self.get_prefix_of_children(document) + children = self._get_prefix_of_children(document) + document.children = children # attributes ############################################################# @property @@ -479,15 +480,14 @@ def by_uid(row): # Sort rows return sorted(rows, key=by_uid) - def get_prefix_of_children(self, document): + def _get_prefix_of_children(self, document): """Return the prefixes of the children of this document.""" for child in self.children: if child.document == document: children = [c.document.prefix for c in child.children] - document.children = children - return + return children children = [c.document.prefix for c in self.children] - document.children = children + return children def _iter_rows(self, item, mapping, parent=True, child=True, row=None): # pylint: disable=R0913 """Generate all traceability row slices. From 7d4b9e44a338b7c3a47e3e548f97802869d4b979 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Tue, 13 Dec 2016 14:08:47 -0500 Subject: [PATCH 17/19] Bump version to 1.1b1 --- CHANGELOG.md | 4 ++++ docs/cli/validation.md | 8 ++++++++ doorstop/__init__.py | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2803005e8..71f6b2c92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Revision History +## 1.1 (unreleased) + +- Added '--strict-child-check' option to ensure links from every child document. + ## 1.0.2 (2016/06/08) - Moved the documentation to [ReadTheDocs](http://doorstop.readthedocs.io). diff --git a/docs/cli/validation.md b/docs/cli/validation.md index 279d0cf9d..8f69fad7a 100644 --- a/docs/cli/validation.md +++ b/docs/cli/validation.md @@ -6,3 +6,11 @@ To check a document hierarchy for consistency, run the main command: $ doorstop valid tree: REQ <- [ TST ] ``` + +## Links + +To confirm that every item in a document links to its parents: + +```sh +$ doorstop --strict-child-check +``` diff --git a/doorstop/__init__.py b/doorstop/__init__.py index e9ad8ae31..92f79df96 100644 --- a/doorstop/__init__.py +++ b/doorstop/__init__.py @@ -3,7 +3,7 @@ import sys __project__ = 'Doorstop' -__version__ = '1.0.2' +__version__ = '1.1b1' CLI = 'doorstop' GUI = 'doorstop-gui' From 46b108d912673a896e5d830d78b6681422137b77 Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Tue, 13 Dec 2016 14:16:38 -0500 Subject: [PATCH 18/19] Update release tooling --- Makefile | 202 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 108 insertions(+), 94 deletions(-) diff --git a/Makefile b/Makefile index 7d34d65d5..bb04d5736 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,7 @@ DEPENDS_CI := $(ENV)/.depends-ci DEPENDS_DEV := $(ENV)/.depends-dev ALL := $(ENV)/.all -# Main Targets ################################################################# +# MAIN TASKS ################################################################### .PHONY: all all: depends $(ALL) @@ -76,7 +76,19 @@ $(ALL): $(SOURCES) $(YAML) .PHONY: ci ci: doorstop pep8 pep257 test tests tutorial -# Development Installation ##################################################### +.PHONY: doorstop +doorstop: env + $(BIN)/doorstop --warn-all --error-all --quiet + +.PHONY: gui +gui: env + $(BIN)/doorstop-gui + +.PHONY: serve +serve: env + $(SUDO) $(BIN)/doorstop-server --debug --launch --port 80 + +# PROJECT DEPENDENCIES ######################################################### .PHONY: env env: .virtualenv $(EGG_INFO) @@ -102,24 +114,56 @@ $(DEPENDS_CI): Makefile .PHONY: depends-dev depends-dev: env Makefile $(DEPENDS_DEV) $(DEPENDS_DEV): Makefile - $(PIP) install --upgrade pip pep8radius pygments docutils readme pdoc mkdocs markdown wheel + $(PIP) install --upgrade pip pep8radius pygments docutils readme pdoc mkdocs markdown wheel twine touch $(DEPENDS_DEV) # flag to indicate dependencies are installed -# Development Usage ############################################################ +# CHECKS ####################################################################### -.PHONY: doorstop -doorstop: env - $(BIN)/doorstop --warn-all --error-all --quiet +.PHONY: check +check: pep8 pep257 pylint -.PHONY: gui -gui: env - $(BIN)/doorstop-gui +.PHONY: pep8 +pep8: depends-ci +# E501: line too long (checked by PyLint) + $(PEP8) $(PACKAGE) --ignore=E501 -.PHONY: serve -serve: env - $(SUDO) $(BIN)/doorstop-server --debug --launch --port 80 +.PHONY: pep257 +pep257: depends-ci + $(PEP257) $(PACKAGE) + +.PHONY: pylint +pylint: depends-ci + $(PYLINT) $(PACKAGE) --rcfile=.pylintrc + +.PHONY: fix +fix: depends-dev + $(PEP8RADIUS) --docformatter --in-place -# Documentation ################################################################ +# TESTS ######################################################################## + +.PHONY: test +test: depends-ci .clean-test + $(NOSE) --config=.noserc +ifndef TRAVIS + $(COVERAGE) html --directory htmlcov --fail-under=$(UNIT_TEST_COVERAGE) +endif + +.PHONY: tests +tests: depends-ci .clean-test + TEST_INTEGRATION=1 $(NOSE) --config=.noserc --cover-package=$(PACKAGE) -xv +ifndef TRAVIS + $(COVERAGE) html --directory htmlcov --fail-under=$(INTEGRATION_TEST_COVERAGE) +endif + +.PHONY: tutorial +tutorial: env + $(PYTHON) $(PACKAGE)/cli/test/test_tutorial.py + +.PHONY: read-coverage +read-coverage: + $(OPEN) htmlcov/index.html + +# DOCUMENTATION ################################################################ .PHONY: doc doc: readme verify-readme uml apidocs mkdocs @@ -184,53 +228,62 @@ mkdocs: depends-dev site/index.html site/index.html: mkdocs.yml docs/*.md $(MKDOCS) build --clean --strict -# Static Analysis ############################################################## +# BUILD ######################################################################## -.PHONY: check -check: pep8 pep257 pylint +PYINSTALLER := $(BIN)/pyinstaller +PYINSTALLER_MAKESPEC := $(BIN)/pyi-makespec -.PHONY: pep8 -pep8: depends-ci -# E501: line too long (checked by PyLint) - $(PEP8) $(PACKAGE) --ignore=E501 +DIST_FILES := dist/*.tar.gz dist/*.whl +EXE_FILES := dist/$(PROJECT).* -.PHONY: pep257 -pep257: depends-ci - $(PEP257) $(PACKAGE) +.PHONY: dist +dist: depends-dev $(DIST_FILES) +$(DIST_FILES): $(MODULES) README.rst CHANGELOG.rst + rm -f $(DIST_FILES) + $(PYTHON) setup.py check --restructuredtext --strict --metadata + $(PYTHON) setup.py sdist + $(PYTHON) setup.py bdist_wheel -.PHONY: pylint -pylint: depends-ci - $(PYLINT) $(PACKAGE) --rcfile=.pylintrc +%.rst: %.md + pandoc -f markdown_github -t rst -o $@ $< -.PHONY: fix -fix: depends-dev - $(PEP8RADIUS) --docformatter --in-place +.PHONY: exe +exe: depends-dev $(EXE_FILES) +$(EXE_FILES): $(MODULES) $(PROJECT).spec + # For framework/shared support: https://github.com/yyuu/pyenv/wiki + $(PYINSTALLER) $(PROJECT).spec --noconfirm --clean -# Testing ###################################################################### +$(PROJECT).spec: + $(PYINSTALLER_MAKESPEC) $(PACKAGE)/__main__.py --onefile --windowed --name=$(PROJECT) -.PHONY: test -test: depends-ci .clean-test - $(NOSE) --config=.noserc -ifndef TRAVIS - $(COVERAGE) html --directory htmlcov --fail-under=$(UNIT_TEST_COVERAGE) -endif +# RELEASE ###################################################################### -.PHONY: tests -tests: depends-ci .clean-test - TEST_INTEGRATION=1 $(NOSE) --config=.noserc --cover-package=$(PACKAGE) -xv -ifndef TRAVIS - $(COVERAGE) html --directory htmlcov --fail-under=$(INTEGRATION_TEST_COVERAGE) -endif +TWINE := $(BIN)/twine -.PHONY: tutorial -tutorial: env - $(PYTHON) $(PACKAGE)/cli/test/test_tutorial.py +.PHONY: register +register: dist ## Register the project on PyPI + @ echo NOTE: your project must be registered manually + @ echo https://github.com/pypa/python-packaging-user-guide/issues/263 + # TODO: switch to twine when the above issue is resolved + # $(TWINE) register dist/*.whl -.PHONY: read-coverage -read-coverage: - $(OPEN) htmlcov/index.html +.PHONY: upload +upload: .git-no-changes register ## Upload the current version to PyPI + $(TWINE) upload dist/* + $(OPEN) https://pypi.python.org/pypi/$(PROJECT) + +.PHONY: .git-no-changes +.git-no-changes: + @ if git diff --name-only --exit-code; \ + then \ + echo Git working copy is clean...; \ + else \ + echo ERROR: Git working copy is dirty!; \ + echo Commit your changes and try again.; \ + exit -1; \ + fi; -# Cleanup ###################################################################### +# CLEANUP ###################################################################### .PHONY: clean clean: .clean-dist .clean-test .clean-doc .clean-build @@ -268,49 +321,10 @@ clean-all: clean clean-env .clean-workspace .clean-workspace: rm -rf *.sublime-workspace -# Release ###################################################################### - -.PHONY: register-test -register-test: doc - $(PYTHON) setup.py register --strict --repository https://testpypi.python.org/pypi - -.PHONY: upload-test -upload-test: register-test - $(PYTHON) setup.py sdist upload --repository https://testpypi.python.org/pypi - $(PYTHON) setup.py bdist_wheel upload --repository https://testpypi.python.org/pypi - $(OPEN) https://testpypi.python.org/pypi/$(PROJECT) - -.PHONY: register -register: doc - $(PYTHON) setup.py register --strict - -.PHONY: upload -upload: .git-no-changes register - $(PYTHON) setup.py sdist upload - $(PYTHON) setup.py bdist_wheel upload - $(OPEN) https://pypi.python.org/pypi/$(PROJECT) - -.PHONY: .git-no-changes -.git-no-changes: - @ if git diff --name-only --exit-code; \ - then \ - echo Git working copy is clean...; \ - else \ - echo ERROR: Git working copy is dirty!; \ - echo Commit your changes and try again.; \ - exit -1; \ - fi; - -# System Installation ########################################################## - -.PHONY: develop -develop: - $(SYS_PYTHON) setup.py develop +# HELP ######################################################################### -.PHONY: install -install: - $(SYS_PYTHON) setup.py install +.PHONY: help +help: all + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -.PHONY: download -download: - $(SYS_PYTHON) -m pip install $(PROJECT) +.DEFAULT_GOAL := help From 9995f517a802b2de0f02063857c12bd5dd93270f Mon Sep 17 00:00:00 2001 From: Jace Browning Date: Mon, 9 Jan 2017 12:54:31 -0500 Subject: [PATCH 19/19] Bump version to 1.1 --- CHANGELOG.md | 2 +- doorstop/__init__.py | 2 +- setup.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71f6b2c92..35823a5d0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Revision History -## 1.1 (unreleased) +## 1.1 (2017/01/09) - Added '--strict-child-check' option to ensure links from every child document. diff --git a/doorstop/__init__.py b/doorstop/__init__.py index 92f79df96..a22ffff34 100644 --- a/doorstop/__init__.py +++ b/doorstop/__init__.py @@ -3,7 +3,7 @@ import sys __project__ = 'Doorstop' -__version__ = '1.1b1' +__version__ = '1.1' CLI = 'doorstop' GUI = 'doorstop-gui' diff --git a/setup.py b/setup.py index 070154de6..727a2c635 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Software Development :: Documentation', 'Topic :: Text Editors :: Documentation', 'Topic :: Text Processing :: Markup',