Retire Packaging Deb project repos
This commit is part of a series to retire the Packaging Deb project. Step 2 is to remove all content from the project repos, replacing it with a README notification where to find ongoing work, and how to recover the repo if needed at some future point (as in https://docs.openstack.org/infra/manual/drivers.html#retiring-a-project). Change-Id: I4d66bc8da65514468dc5d56b9003a639b6f48ac8
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
[run]
|
||||
branch = True
|
||||
source = reno
|
||||
omit = reno/openstack/*
|
||||
|
||||
[report]
|
||||
ignore_errors = True
|
56
.gitignore
vendored
56
.gitignore
vendored
@@ -1,56 +0,0 @@
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
.eggs
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
lib
|
||||
lib64
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage*
|
||||
.tox
|
||||
nosetests.xml
|
||||
.testrepository
|
||||
.venv
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
||||
# Mr Developer
|
||||
.mr.developer.cfg
|
||||
.project
|
||||
.pydevproject
|
||||
|
||||
# Complexity
|
||||
output/*.html
|
||||
output/*/index.html
|
||||
|
||||
# Sphinx
|
||||
doc/build
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.*sw?
|
||||
/cover/
|
||||
/releasenotes/notes/reno.cache
|
@@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/reno.git
|
3
.mailmap
3
.mailmap
@@ -1,3 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
@@ -1,7 +0,0 @@
|
||||
[DEFAULT]
|
||||
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
|
||||
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
|
||||
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
|
||||
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
|
||||
test_id_option=--load-list $IDFILE
|
||||
test_list_option=--list
|
@@ -1,14 +0,0 @@
|
||||
If you would like to contribute to the development of OpenStack, you must
|
||||
follow the steps in this page:
|
||||
https://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
If you already have a good understanding of how the system works and your
|
||||
OpenStack accounts are set up, you can skip to the development workflow
|
||||
section of this documentation to learn how changes to OpenStack should be
|
||||
submitted for review via the Gerrit tool:
|
||||
https://docs.openstack.org/infra/manual/developers.html#development-workflow
|
||||
|
||||
Pull requests submitted through GitHub will be ignored.
|
||||
|
||||
Bugs should be filed on Launchpad, not GitHub:
|
||||
https://bugs.launchpad.net/reno
|
@@ -1,4 +0,0 @@
|
||||
reno Style Commandments
|
||||
===============================================
|
||||
|
||||
Read the OpenStack Style Commandments https://docs.openstack.org/developer/hacking/
|
176
LICENSE
176
LICENSE
@@ -1,176 +0,0 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
@@ -1,6 +0,0 @@
|
||||
include AUTHORS
|
||||
include ChangeLog
|
||||
exclude .gitignore
|
||||
exclude .gitreview
|
||||
|
||||
global-exclude *.pyc
|
14
README
Normal file
14
README
Normal file
@@ -0,0 +1,14 @@
|
||||
This project is no longer maintained.
|
||||
|
||||
The contents of this repository are still available in the Git
|
||||
source code management system. To see the contents of this
|
||||
repository before it reached its end of life, please check out the
|
||||
previous commit with "git checkout HEAD^1".
|
||||
|
||||
For ongoing work on maintaining OpenStack packages in the Debian
|
||||
distribution, please see the Debian OpenStack packaging team at
|
||||
https://wiki.debian.org/OpenStack/.
|
||||
|
||||
For any further questions, please email
|
||||
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||
Freenode.
|
58
README.rst
58
README.rst
@@ -1,58 +0,0 @@
|
||||
=========================================
|
||||
reno: A New Way to Manage Release Notes
|
||||
=========================================
|
||||
|
||||
Reno is a release notes manager designed with high throughput in mind,
|
||||
supporting fast distributed development teams without introducing
|
||||
additional development processes. Our goal is to encourage detailed
|
||||
and accurate release notes for every release.
|
||||
|
||||
Reno uses git to store its data, along side the code being
|
||||
described. This means release notes can be written when the code
|
||||
changes are fresh, so no details are forgotten. It also means that
|
||||
release notes can go through the same review process used for managing
|
||||
code and other documentation changes.
|
||||
|
||||
Reno stores each release note in a separate file to enable a large
|
||||
number of developers to work on multiple patches simultaneously, all
|
||||
targeting the same branch, without worrying about merge
|
||||
conflicts. This cuts down on the need to rebase or otherwise manually
|
||||
resolve conflicts, and keeps a development team moving quickly.
|
||||
|
||||
Reno also supports multiple branches, allowing release notes to be
|
||||
back-ported from master to maintenance branches together with the
|
||||
code for bug fixes.
|
||||
|
||||
Reno organizes notes into logical groups based on whether they
|
||||
describe new features, bug fixes, known issues, or other topics of
|
||||
interest to the user. Contributors categorize individual notes as they
|
||||
are added, and reno combines them before publishing.
|
||||
|
||||
Notes can be styled using reStructuredText directives, and reno's
|
||||
Sphinx integration makes it easy to incorporate release notes into
|
||||
automated documentation builds.
|
||||
|
||||
Notes are automatically associated with the release version based on
|
||||
the git tags applied to the repository, so it is not necessary to
|
||||
track changes manually using a bug tracker or other tool, or to worry
|
||||
that an important change will be missed when the release notes are
|
||||
written by hand all at one time, just before a release.
|
||||
|
||||
Modifications to notes are incorporated when the notes are shown in
|
||||
their original location in the history. This feature makes it possible
|
||||
to correct typos or otherwise fix a published release note after a
|
||||
release is made, but have the new note content associated with the
|
||||
original version number. Notes also can be deleted, eliminating them
|
||||
from future documentation builds.
|
||||
|
||||
Project Meta-data
|
||||
=================
|
||||
|
||||
.. .. image:: https://governance.openstack.org/badges/reno.svg
|
||||
:target: https://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
* Free software: Apache license
|
||||
* Documentation: https://docs.openstack.org/reno/latest/
|
||||
* Source: https://git.openstack.org/cgit/openstack/reno
|
||||
* Bugs: https://bugs.launchpad.net/reno
|
||||
* IRC: #openstack-release on freenode
|
@@ -1,98 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
# oslosphinx uses reno and reno uses oslosphinx. Make oslosphinx for
|
||||
# reno optional to break the build cycle
|
||||
try:
|
||||
import openstackdocstheme
|
||||
except:
|
||||
has_theme = False
|
||||
else:
|
||||
has_theme = True
|
||||
|
||||
|
||||
sys.path.insert(0, os.path.abspath('../..'))
|
||||
# -- General configuration ----------------------------------------------------
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = [
|
||||
'sphinx.ext.autodoc',
|
||||
# 'sphinx.ext.intersphinx',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
if has_theme:
|
||||
extensions.append('openstackdocstheme')
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# openstackdocstheme options
|
||||
repository_name = 'openstack/reno'
|
||||
bug_project = 'reno'
|
||||
bug_tag = ''
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# autodoc generation is a bit aggressive and a nuisance when doing heavy
|
||||
# text edit cycles.
|
||||
# execute "export SPHINX_DEBUG=1" in your terminal to disable
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'reno'
|
||||
copyright = u'2013, OpenStack Foundation'
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
add_module_names = True
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# Do not warn about non-local image URI
|
||||
suppress_warnings = ['image.nonlocal_uri']
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
||||
# html_theme_path = ["."]
|
||||
# html_theme = '_theme'
|
||||
# html_static_path = ['static']
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = '%sdoc' % project
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass
|
||||
# [howto/manual]).
|
||||
latex_documents = [
|
||||
('index',
|
||||
'%s.tex' % project,
|
||||
u'%s Documentation' % project,
|
||||
u'OpenStack Foundation', 'manual'),
|
||||
]
|
||||
|
||||
# Example configuration for intersphinx: refer to the Python standard library.
|
||||
# intersphinx_mapping = {'http://docs.python.org/': None}
|
@@ -1,5 +0,0 @@
|
||||
============
|
||||
Contributing
|
||||
============
|
||||
|
||||
.. include:: ../../../CONTRIBUTING.rst
|
@@ -1,12 +0,0 @@
|
||||
.. include:: ../../README.rst
|
||||
|
||||
Contents
|
||||
========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
user/index
|
||||
install/index
|
||||
contributor/index
|
||||
releasenotes/index
|
@@ -1,15 +0,0 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
At the command line::
|
||||
|
||||
$ pip install reno
|
||||
|
||||
Sphinx Extension
|
||||
================
|
||||
|
||||
To use the Sphinx extension built into reno, install the ``[sphinx]``
|
||||
extra dependencies::
|
||||
|
||||
$ pip install 'reno[sphinx]'
|
@@ -1,12 +0,0 @@
|
||||
===============
|
||||
Release Notes
|
||||
===============
|
||||
|
||||
.. release-notes:: Unreleased
|
||||
|
||||
.. release-notes:: Mainline
|
||||
:branch: origin/master
|
||||
|
||||
.. release-notes:: Newton Series
|
||||
:branch: origin/stable/newton
|
||||
:earliest-version: 1.9.0
|
@@ -1,52 +0,0 @@
|
||||
=====================================
|
||||
Design Constraints and Requirements
|
||||
=====================================
|
||||
|
||||
Managing release notes for a complex project over a long period of
|
||||
time with many releases can be time consuming and error prone. Reno
|
||||
helps automate the hard parts by devising a way to store the notes
|
||||
inside the git repository where they can be tagged as part of the
|
||||
release.
|
||||
|
||||
We had several design inputs:
|
||||
|
||||
* Release notes should be part of the git history, so as fixes in
|
||||
master are back-ported to older branches the notes can go with the
|
||||
code change.
|
||||
* Release notes may need to change over time, as typos are found,
|
||||
logical errors or confusing language needs to be fixed, or as more
|
||||
information becomes available (CVE numbers, etc.).
|
||||
* Release notes should be peer-reviewed, as with other documentation
|
||||
and code changes.
|
||||
* Notes are mutable in that a clone today vs a clone tomorrow might
|
||||
have different release notes about the same change.
|
||||
* Notes are immutable in that for a given git hash/tag the release
|
||||
notes will be the same. Tagging a commit will change the version
|
||||
description but that is all.
|
||||
* We want to avoid merge issues when shepherding in a lot of
|
||||
release-note-worthy changes, which we expect to happen on stable
|
||||
branches always, and at release times on master branches.
|
||||
* We want writing a release note to be straight-forward.
|
||||
* We do not want release notes to be custom ordered within a release,
|
||||
but we do want the ordering to be predictable and consistent.
|
||||
* We must be able to entirely remove a release note.
|
||||
* We must not make things progressively slow down to a crawl over
|
||||
years of usage.
|
||||
* Release note authors shouldn't need to know any special values for
|
||||
naming their notes files (i.e., no change id or SHA value that has
|
||||
special meaning).
|
||||
* It would be nice if it was somewhat easy to identify the file
|
||||
containing a release note on a particular topic.
|
||||
* Release notes should be grouped by type in the output document.
|
||||
|
||||
1. New features
|
||||
2. Known issues
|
||||
3. Upgrade notes
|
||||
4. Security fixes
|
||||
5. Bugs fixes
|
||||
6. Other
|
||||
|
||||
We want to eventually provide the ability to create a release notes
|
||||
file for a given release and add it to the source distribution for the
|
||||
project. As a first step, we are going to settle for publishing
|
||||
release notes in the documentation for a project.
|
@@ -1,17 +0,0 @@
|
||||
==========
|
||||
Examples
|
||||
==========
|
||||
|
||||
Input file
|
||||
==========
|
||||
|
||||
.. literalinclude:: ../../../examples/notes/add-complex-example-6b5927c246456896.yaml
|
||||
:caption: examples/notes/add-complex-example-6b5927c246456896.yaml
|
||||
:language: yaml
|
||||
|
||||
Rendered
|
||||
========
|
||||
|
||||
.. release-notes::
|
||||
:relnotessubdir: examples
|
||||
:earliest-version: 1.0.0
|
@@ -1,11 +0,0 @@
|
||||
=================
|
||||
reno User Guide
|
||||
=================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
design
|
||||
usage
|
||||
sphinxext
|
||||
examples
|
@@ -1,88 +0,0 @@
|
||||
==================
|
||||
Sphinx Extension
|
||||
==================
|
||||
|
||||
In addition to the command line tool, reno includes a Sphinx extension
|
||||
for incorporating release notes for a project in its documentation
|
||||
automatically.
|
||||
|
||||
Enable the extension by adding ``'reno.sphinxext'`` to the
|
||||
``extensions`` list in the Sphinx project ``conf.py`` file.
|
||||
|
||||
.. rst:directive:: release-notes
|
||||
|
||||
The ``release-notes`` directive accepts the same inputs as the
|
||||
``report`` subcommand, and inserts the report inline into the
|
||||
current document where Sphinx then processes it to create HTML,
|
||||
PDF, or other output formats.
|
||||
|
||||
If the directive has a body, it is used to create a title entry
|
||||
with ``=`` over and under lines (the typical heading style for the
|
||||
top-level heading in a document).
|
||||
|
||||
Options:
|
||||
|
||||
*branch*
|
||||
The name of the branch to scan. Defaults to the current branch.
|
||||
|
||||
*reporoot*
|
||||
The path to the repository root directory. Defaults to the
|
||||
directory where ``sphinx-build`` is being run.
|
||||
|
||||
*relnotessubdir*
|
||||
The path under ``reporoot`` where the release notes are. Defaults
|
||||
to ``releasenotes``.
|
||||
|
||||
*notesdir*
|
||||
The path under ``relnotessubdir`` where the release notes
|
||||
are. Defaults to ``notes``.
|
||||
|
||||
*version*
|
||||
A comma separated list of versions to include in the notes. The
|
||||
default is to include all versions found on ``branch``.
|
||||
|
||||
*collapse-pre-releases*
|
||||
A flag indicating that notes attached to pre-release versions
|
||||
should be incorporated into the notes for the final release,
|
||||
after the final release is tagged.
|
||||
|
||||
*earliest-version*
|
||||
A string containing the version number of the earliest version to
|
||||
be included. For example, when scanning a branch, this is
|
||||
typically set to the version used to create the branch to limit
|
||||
the output to only versions on that branch.
|
||||
|
||||
*ignore-notes*
|
||||
A string containing a comma-delimited list of filenames or UIDs
|
||||
for notes that should be ignored by the scanner. It is most
|
||||
useful to set this when a note is edited on the wrong branch,
|
||||
making it appear to be part of a release that it is not.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
The release notes for the "current" branch, with "Release Notes" as a
|
||||
title.
|
||||
|
||||
.. code-block:: rest
|
||||
|
||||
.. release-notes:: Release Notes
|
||||
|
||||
The release notes for the "stable/liberty" branch, with a separate
|
||||
title.
|
||||
|
||||
.. code-block:: rest
|
||||
|
||||
=======================
|
||||
Liberty Release Notes
|
||||
=======================
|
||||
|
||||
.. release-notes::
|
||||
:branch: stable/liberty
|
||||
|
||||
The release notes for version "1.0.0".
|
||||
|
||||
.. code-block:: rest
|
||||
|
||||
.. release-notes:: 1.0.0 Release Notes
|
||||
:version: 1.0.0
|
@@ -1,325 +0,0 @@
|
||||
========
|
||||
Usage
|
||||
========
|
||||
|
||||
Creating New Release Notes
|
||||
==========================
|
||||
|
||||
The ``reno`` command line tool is used to create a new release note
|
||||
file in the correct format and with a unique name. The ``new``
|
||||
subcommand combines a random suffix with a "slug" value to create
|
||||
the file with a unique name that is easy to identify again later.
|
||||
|
||||
::
|
||||
|
||||
$ reno new slug-goes-here
|
||||
Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
|
||||
|
||||
Within OpenStack projects, ``reno`` is often run via tox instead of
|
||||
being installed globally. For example
|
||||
|
||||
::
|
||||
|
||||
$ tox -e venv -- reno new slug-goes-here
|
||||
venv develop-inst-nodeps: /mnt/projects/release-notes-generation/reno
|
||||
venv runtests: commands[0] | reno new slug-goes-here
|
||||
Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
|
||||
venv: commands succeeded
|
||||
congratulations :)
|
||||
$ git status
|
||||
Untracked files:
|
||||
(use "git add <file>..." to include in what will be committed)
|
||||
|
||||
releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
|
||||
|
||||
The ``--edit`` option opens the new note in a text editor.
|
||||
|
||||
::
|
||||
|
||||
$ reno new slug-goes-here --edit
|
||||
... Opens the editor set in the EDITOR environment variable, editing the new file ...
|
||||
Created new notes file in releasenotes/notes/slug-goes-here-95915aaedd3c48d8.yaml
|
||||
|
||||
|
||||
By default, the new note is created under ``./releasenotes/notes``.
|
||||
The ``--rel-notes-dir`` command-line flag changes the parent directory
|
||||
(the ``notes`` subdirectory is always appended). It's also possible to
|
||||
set a custom template to create notes (see `Configuring Reno`_ ).
|
||||
|
||||
Editing a Release Note
|
||||
======================
|
||||
|
||||
The note file is a YAML file with several sections. All of the text is
|
||||
interpreted as having `reStructuredText`_ formatting. The permitted
|
||||
sections are configurable (see below) but default to the following
|
||||
list:
|
||||
|
||||
prelude
|
||||
General comments about the release. Prelude sections from all notes in a
|
||||
release are combined, in note order, to produce a single prelude
|
||||
introducing that release. This section is always included, regardless
|
||||
of what sections are configured.
|
||||
|
||||
features
|
||||
A list of new major features in the release.
|
||||
|
||||
issues
|
||||
A list of known issues in the release. For example, if a new driver
|
||||
is experimental or known to not work in some cases, it should be
|
||||
mentioned here.
|
||||
|
||||
upgrade
|
||||
A list of upgrade notes in the release. For example, if a database
|
||||
schema alteration is needed.
|
||||
|
||||
deprecations
|
||||
A list of features, APIs, configuration options to be deprecated in the
|
||||
release. Deprecations should not be used for something that is removed in the
|
||||
release, use upgrade section instead. Deprecation should allow time for users
|
||||
to make necessary changes for the removal to happen in a future release.
|
||||
|
||||
critical
|
||||
A list of *fixed* critical bugs.
|
||||
|
||||
security
|
||||
A list of *fixed* security issues.
|
||||
|
||||
fixes
|
||||
A list of other *fixed* bugs.
|
||||
|
||||
other
|
||||
Other notes that are important but do not fall into any of the given
|
||||
categories.
|
||||
|
||||
Any sections that would be blank should be left out of the note file
|
||||
entirely.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
---
|
||||
prelude: >
|
||||
Replace this text with content to appear at the
|
||||
top of the section for this release.
|
||||
features:
|
||||
- List new features here, or remove this section.
|
||||
issues:
|
||||
- List known issues here, or remove this section.
|
||||
upgrade:
|
||||
- List upgrade notes here, or remove this section.
|
||||
deprecations:
|
||||
- List deprecation notes here, or remove this section
|
||||
critical:
|
||||
- Add critical notes here, or remove this section.
|
||||
security:
|
||||
- Add security notes here, or remove this section.
|
||||
fixes:
|
||||
- Add normal bug fixes here, or remove this section.
|
||||
other:
|
||||
- Add other notes here, or remove this section.
|
||||
|
||||
Note File Syntax
|
||||
----------------
|
||||
|
||||
Release notes may include embedded `reStructuredText`_, including simple
|
||||
inline markup like emphasis and pre-formatted text as well as complex
|
||||
body structures such as nested lists and tables. To use these
|
||||
formatting features, the note must be escaped from the YAML parser.
|
||||
|
||||
The default template sets up the ``prelude`` section to use ``>`` so
|
||||
that line breaks in the text are removed. This escaping mechanism is
|
||||
not needed for the bullet items in the other sections of the template.
|
||||
|
||||
To escape the text of any section and *retain* the newlines, prefix
|
||||
the value with ``|``. For example:
|
||||
|
||||
.. literalinclude:: ../../../examples/notes/add-complex-example-6b5927c246456896.yaml
|
||||
:language: yaml
|
||||
|
||||
See :doc:`examples` for the rendered version of the note.
|
||||
|
||||
.. _reStructuredText: http://www.sphinx-doc.org/en/stable/rest.html
|
||||
|
||||
Generating a Report
|
||||
===================
|
||||
|
||||
Run ``reno report <path-to-git-repository>`` to generate a report
|
||||
containing the release notes. The ``--branch`` argument can be used to
|
||||
generate a report for a specific branch (the default is the branch
|
||||
that is checked out). To limit the report to a subset of the available
|
||||
versions on the branch, use the ``--version`` option (it can be
|
||||
repeated).
|
||||
|
||||
Notes are output in the order they are found when scanning the git
|
||||
history of the branch using topological ordering. This is
|
||||
deterministic, but not necessarily predictable or mutable.
|
||||
|
||||
Checking Notes
|
||||
==============
|
||||
|
||||
Run ``reno lint <path-to-git-repository>`` to test the existing
|
||||
release notes files against some rules for catching common
|
||||
mistakes. The command exits with an error code if there are any
|
||||
mistakes, so it can be used in a build pipeline to force some
|
||||
correctness.
|
||||
|
||||
Configuring Reno
|
||||
================
|
||||
|
||||
Reno looks for an optional config file, either ``config.yaml`` in the release
|
||||
notes directory or ``reno.yaml`` in the root directory. If the values in the
|
||||
configuration file do not apply to the command being run, they are ignored. For
|
||||
example, some reno commands take inputs controlling the branch, earliest
|
||||
revision, and other common parameters that control which notes are included in
|
||||
the output. Because they are commonly set options, a configuration file may be
|
||||
the most convenient way to manage the values consistently.
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
---
|
||||
branch: master
|
||||
earliest_version: 12.0.0
|
||||
collapse_pre_releases: false
|
||||
stop_at_branch_base: true
|
||||
sections:
|
||||
# The prelude section is implicitly included.
|
||||
- [features, New Features]
|
||||
- [issues, Known Issues]
|
||||
- [upgrade, Upgrade Notes]
|
||||
- [api, API Changes]
|
||||
- [security, Security Issues]
|
||||
- [fixes, Bug Fixes]
|
||||
# Change prelude_section_name to 'release_summary' from default value
|
||||
# 'prelude'.
|
||||
prelude_section_name: release_summary
|
||||
template: |
|
||||
<template-used-to-create-new-notes>
|
||||
...
|
||||
|
||||
Many of the settings in the configuration file can be overridden by
|
||||
using command-line switches. For example:
|
||||
|
||||
- ``--branch``
|
||||
- ``--earliest-version``
|
||||
- ``--collapse-pre-releases``/``--no-collapse-pre-releases``
|
||||
- ``--ignore-cache``
|
||||
- ``--stop-at-branch-base``/``--no-stop-at-branch-base``
|
||||
|
||||
The following options are configurable:
|
||||
|
||||
`notesdir`
|
||||
The notes subdirectory within the `relnotesdir` where the notes live.
|
||||
|
||||
Defaults to ``notes``.
|
||||
|
||||
`collapse_pre_releases`
|
||||
Should pre-release versions be merged into the final release of the same
|
||||
number (`1.0.0.0a1` notes appear under `1.0.0`).
|
||||
|
||||
Defaults to ``True``.
|
||||
|
||||
`stop_at_branch_base`
|
||||
Should the scanner stop at the base of a branch (True) or go ahead and scan
|
||||
the entire history (False)?
|
||||
|
||||
Defaults to ``True``.
|
||||
|
||||
`branch`
|
||||
The git branch to scan. If a stable branch is specified but does not exist,
|
||||
reno attempts to automatically convert that to an "end-of-life" tag. For
|
||||
example, ``origin/stable/liberty`` would be converted to ``liberty-eol``.
|
||||
|
||||
Defaults to the "current" branch checked out.
|
||||
|
||||
`earliest_version`
|
||||
The earliest version to be included. This is usually the lowest version
|
||||
number, and is meant to be the oldest version. If unset, all versions will be
|
||||
scanned.
|
||||
|
||||
Defaults to ``None``.
|
||||
|
||||
`template`
|
||||
The template used by reno new to create a note.
|
||||
|
||||
`release_tag_re`
|
||||
The regex pattern used to match the repo tags representing a valid release
|
||||
version. The pattern is compiled with the verbose and unicode flags enabled.
|
||||
|
||||
Defaults to ``((?:[\d.ab]|rc)+)``.
|
||||
|
||||
`pre_release_tag_re`
|
||||
The regex pattern used to check if a valid release version tag is also a
|
||||
valid pre-release version. The pattern is compiled with the verbose and
|
||||
unicode flags enabled. The pattern must define a group called `pre_release`
|
||||
that matches the pre-release part of the tag and any separator, e.g for
|
||||
pre-release version `12.0.0.0rc1` the default RE pattern will identify
|
||||
`.0rc1` as the value of the group 'pre_release'.
|
||||
|
||||
Defaults to ``(?P<pre_release>\.\d+(?:[ab]|rc)+\d*)$``.
|
||||
|
||||
`branch_name_re`
|
||||
The pattern for names for branches that are relevant when scanning history to
|
||||
determine where to stop, to find the "base" of a branch. Other branches are
|
||||
ignored.
|
||||
|
||||
Defaults to ``stable/.+``.
|
||||
|
||||
`sections`
|
||||
The identifiers and names of permitted sections in the release notes, in the
|
||||
order in which the final report will be generated. A prelude section will
|
||||
always be automatically inserted before the first element of this list.
|
||||
|
||||
`prelude_section_name`
|
||||
The name of the prelude section in the note template. Note that the
|
||||
value for this must be a single word, but can have underscores. The
|
||||
value is displayed in titlecase in the report after replacing
|
||||
underscores with spaces.
|
||||
|
||||
Defaults to ``prelude``
|
||||
|
||||
`ignore_null_merges`
|
||||
OpenStack used to use null-merges to bring final release tags from
|
||||
stable branches back into the master branch. This confuses the
|
||||
regular traversal because it makes that stable branch appear to be
|
||||
part of master and/or the later stable branch. This option allows us
|
||||
to ignore those.
|
||||
|
||||
When this option is set to True, any merge commits with no changes
|
||||
and in which the second or later parent is tagged are considered
|
||||
"null-merges" that bring the tag information into the current branch
|
||||
but nothing else.
|
||||
|
||||
Defaults to ``True``.
|
||||
|
||||
`ignore_notes`
|
||||
A list of filenames or UIDs for notes that should be ignored by the
|
||||
reno scanner. It is most useful to set this when a note is edited on
|
||||
the wrong branch, making it appear to be part of a release that it
|
||||
is not.
|
||||
|
||||
.. warning::
|
||||
|
||||
Setting the option in the main configuration file makes it apply
|
||||
to all branches. To ignore a note in the HTML build, use the
|
||||
``ignore-notes`` parameter to the ``release-notes`` sphinx
|
||||
directive.
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
The true location of formatting errors in release notes may be masked
|
||||
because of the way release notes are included into sphinx documents.
|
||||
To generate the release notes manually, so that they can be put into a
|
||||
sphinx document directly for debugging, use the ``report`` command.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ reno report .
|
||||
|
||||
Within OpenStack
|
||||
================
|
||||
|
||||
The OpenStack project maintains separate instructions for configuring
|
||||
the CI jobs and other project-specific settings used for reno. Refer
|
||||
to the `Managing Release Notes
|
||||
<https://docs.openstack.org/project-team-guide/release-management.html#managing-release-notes>`__
|
||||
section of the Project Team Guide for details.
|
@@ -1,37 +0,0 @@
|
||||
---
|
||||
prelude: |
|
||||
This paragraph will
|
||||
retain its newlines
|
||||
when the value is passed to the
|
||||
reStructuredText parser, which
|
||||
will then merge them into
|
||||
a single paragraph without
|
||||
breaks.
|
||||
|
||||
| These
|
||||
| lines
|
||||
| are prefixed
|
||||
| with | so the reStructuredText
|
||||
| parser will retain
|
||||
| the line breaks.
|
||||
features:
|
||||
This note is a simple string, and does not retain its
|
||||
formatting when it is rendered in HTML. rst markup here
|
||||
may break the YAML parser, since the string is not escaped.
|
||||
fixes:
|
||||
- Use YAML lists to add multiple items to the same section.
|
||||
- Another fix could be listed here.
|
||||
other:
|
||||
- |
|
||||
This bullet item includes a paragraph and a nested list,
|
||||
which works because the content of the YAML list item
|
||||
is an escaped string block with reStructuredText formatting.
|
||||
|
||||
* list item 1
|
||||
* list item 2
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
This example is also rendered
|
||||
correctly on multiple lines
|
||||
as a pre-formatted block.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Reno now supports having a ``config.yaml`` file in your release notes
|
||||
directory. It will search for file in the directory specified by
|
||||
``--rel-notes-dir`` and parse it. It will apply whatever options are
|
||||
valid for that particular command. If an option is not relevant to a
|
||||
particular sub-command, it will not attempt to apply them.
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Added a new section for deprecations that occur during a release
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add the ability to limit queries by stopping at an "earliest
|
||||
version". This is intended to be used when scanning a branch, for
|
||||
example, to stop at a point when the branch was created and not
|
||||
include all of the history from the parent branch.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a ``lint`` command for checking the contents and names of the
|
||||
release notes files against some basic validation rules.
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add the ``--verbose``, ``-v``, and ``-q`` options to the command line tool for producing different levels of debug output.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix a problem with branch references so that it is now possible to
|
||||
use a local tracking branch name when the branch only exists on
|
||||
the 'origin' remote. For example, this allows references to
|
||||
'stable/ocata' when there is no local branch with that name but
|
||||
there is an 'origin/stable/ocata' branch.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix a problem caused by failing to process multiple files with the
|
||||
same UID portion of the filename. Ignore existing cases as long as
|
||||
there is a corrective patch to remove them. Prevent new cases from
|
||||
being introduced. See https://bugs.launchpad.net/reno/+bug/1688042
|
||||
for details.
|
@@ -1,12 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Explicitly allow reno to scan starting from a tag by specifying the
|
||||
tag where a branch name would otherwise be used.
|
||||
- |
|
||||
Add logic to allow reno to detect a branch that has been marked as
|
||||
end-of-life using the OpenStack community's process of tagging the
|
||||
HEAD of a stable/foo branch foo-eol before deleting the
|
||||
branch. This means that references to "stable/foo" are translated
|
||||
to "foo-eol" when the branch does not exist, and that Sphinx
|
||||
directives do not need to be manually updated.
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Resolves `a bug <https://bugs.launchpad.net/reno/+bug/1537451>`__
|
||||
with properly detecting pre-release versions in the existing
|
||||
history of a repository that resulted in some release notes not
|
||||
appearing in the report output.
|
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add a flag to collapse pre-release notes into their final release,
|
||||
if the final release tag is present.
|
@@ -1,15 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a configuration option ``branch_name_re`` to hold a regular expression
|
||||
for choosing "interesting" branches when trying to automatically detect
|
||||
how far back the scanner should look. The default is ``stable/.+``, which
|
||||
works for the OpenStack practice of creating branches named after the
|
||||
stable series of releases.
|
||||
fixes:
|
||||
- |
|
||||
Fixes the logic for determining how far back in history to look when
|
||||
scanning a given branch. Reno now looks for the base of the "previous"
|
||||
branch, as determined by looking at branches matching ``branch_name_re``
|
||||
in lexical order. This may not work if branches are created using
|
||||
version numbers as their names.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a configuration option ``sections`` to hold the list of
|
||||
permitted section identifiers and corresponding display names.
|
||||
This also determines the order in which sections are collated.
|
||||
|
@@ -1,15 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add the ability to specify regular expressions to a define a
|
||||
customised versioning scheme for release tags and pre-release tags.
|
||||
|
||||
By default this change supports the current versioning scheme used by
|
||||
OpenStack.
|
||||
|
||||
To customise, update the config.yaml file with the appropriate values.
|
||||
For example, for tags with versions like ``v1.0.0`` and pre-release
|
||||
versions like ``v1.0.0rc1`` the following could be added to config.yaml::
|
||||
|
||||
release_tag_re: 'v\d\.\d\.\d(rc\d+)?'
|
||||
pre_release_tag_re: '(?P<pre_release>rc\d+$)'
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Set the default value of the reporoot argument
|
||||
for all command line programs to "." and make
|
||||
it an optional parameter.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
prelude: >
|
||||
This release includes a significant rewrite of the internal logic of
|
||||
reno to access git data through the dulwich library instead of the
|
||||
git command line porcelain.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
prelude: >
|
||||
This is the first release.
|
||||
features:
|
||||
- Creating new notes files with unique names.
|
||||
- Listing the files with notes related to each release.
|
||||
- Producing a unified report of all of the notes for a release.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fix a problem with the way reno automatically detects the initial
|
||||
version in a branch that prevented it from including all of the
|
||||
notes associated with a release, especially if the branch was
|
||||
created at a pre-release version number.
|
||||
`Bug #1652092 <https://bugs.launchpad.net/reno/+bug/1652092>`__
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Correct a problem with handling deleted release notes that
|
||||
triggered a TypeError with a message like "Can't mix strings and
|
||||
bytes in path components"
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fixes `bug 1522153
|
||||
<https://bugs.launchpad.net/reno/+bug/1522153>`__ so that notes
|
||||
added in commits that are merged after tags are associated with
|
||||
the correct version.
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fixed the section used in the report to include the prelude in the output.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fix `bug 1517175 <https://bugs.launchpad.net/reno/+bug/1517175>`__ to
|
||||
ensure that all tagged versions are detected and that notes are associated
|
||||
with the correct version numbers.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Fixes a problem with the sphinx extension that caused it to
|
||||
produce an error if it had a list of versions to include that were
|
||||
not within the set that seemed to be on the branch because of the
|
||||
branch-base detection logic. Now if a list of versions is
|
||||
included, the scan always includes the full history.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
Release notes entries may now be made up of single strings. This
|
||||
simplifies formatting for smaller notes, and eliminates a class of
|
||||
errors associated with escaping reStructuredText inside YAML lists.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a new configuration option ``ignore_notes``. Setting the value
|
||||
to a list of filenames or UIDs for notes causes the reno scanner
|
||||
to ignore them. It is most useful to set this when a note is
|
||||
edited on the wrong branch, making it appear to be part of a
|
||||
release that it is not.
|
@@ -1,18 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
By default, reno now ignores "null" merge commits that bring in
|
||||
tags from other threads. The new configuration option
|
||||
``ignore_null_merges`` controls this behavior. Setting the flag to
|
||||
False restores the previous behavior in which the null-merge
|
||||
commits were traversed like any other merge commit.
|
||||
upgrade:
|
||||
- |
|
||||
The new configuration option ``ignore_null_merges`` causes the
|
||||
scanner to ignore merge commits with no changes when one of the
|
||||
parents being merged in has a release tag on it.
|
||||
fixes:
|
||||
- |
|
||||
This release fixes a problem with the scanner that may have caused
|
||||
it to stop scanning a branch prematurely when the tag from another
|
||||
branch had been merged into the history.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Include the local working copy when scanning the history of the
|
||||
current branch. Notes files must at least be staged to indicate
|
||||
that they will eventually be part of the history, but subsequent
|
||||
changes to the file do not need to also be staged to be seen.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Sphinx 1.6.1 now interprets error and warning log messages as
|
||||
reasons to abort the build when strict mode is enabled. This
|
||||
release changes the log level for some calls that weren't really
|
||||
errors to begin with to avoid having Sphinx abort the build
|
||||
unnecessarily.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a ``--no-show-source`` option to the report command to skip
|
||||
including the note reference file names and SHA information
|
||||
in comments in the output. This restores the previous format of
|
||||
the output for cases where it is meant to be read by people directly,
|
||||
not just converted to HTML.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- |
|
||||
Remove an infinite loop in the traversal algorithm caused by some
|
||||
null-merge skip situations.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- The oslosphinx dependency for building documentation
|
||||
is now optional. This breaks a build cycle between
|
||||
oslosphinx and reno.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
reno will now scan for a ``reno.yaml`` file in the root repo directory if a
|
||||
``config.yaml`` file does not exist in the releasenotes directory. This
|
||||
allows users to do away with the unnecessary ``notes`` subdirectory in the
|
||||
releasenotes directory.
|
@@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add a ``--title`` option to the report command.
|
@@ -1,9 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
Change the order of the slug and UUID value in the note filename so
|
||||
the slug comes before the UUID to make tab completion easier to
|
||||
use.
|
||||
|
||||
Older files are still supported, and can be renamed to use
|
||||
the new style.
|
@@ -1,8 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The scanner for the "current" branch (usually ``master``) now stops
|
||||
when it encounters the base of an earlier branch matching the
|
||||
``branch_name_re`` config option. This results in less history
|
||||
appearing on the unreleased pages, while still actually showing
|
||||
the current series and any unreleased notes.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The report output now includes debugging details with the filename
|
||||
and sha for the version of the content used to indicate where the
|
||||
content is from to assist with debugging formatting or content
|
||||
issues.
|
@@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add the sphinx extension for integration with documentation builds.
|
@@ -1,7 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Automatically stop scanning branches at the point where they
|
||||
diverge from master. This avoids having release notes from older
|
||||
versions, that appear on master before the branch, from showing up
|
||||
in the versions from the branch. This logic is only applied to
|
||||
branches created from master.
|
@@ -1,9 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Add a new configuration option, stop_at_branch_base, to control
|
||||
whether or not the scanner stops looking for changes at the point
|
||||
where a branch diverges from master. The default is True, meaning
|
||||
that the scanner does stop. A false value means that versions that
|
||||
appear on master from a point earlier than when the branch was
|
||||
created will be included when scanning the branch for release
|
||||
notes.
|
@@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Reno now supports to set through ``template`` attribute in
|
||||
``config.yaml`` a custom template which will be used by reno new
|
||||
to create notes.
|
@@ -1,5 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Reno now enables with reno new ``--edit`` to create a note and edit it with
|
||||
your editor (defined with EDITOR environment variable).
|
@@ -1,13 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Support note entries that span multiple lines using
|
||||
preformatted syntax in YAML by prefixing the
|
||||
list entry with ``|``.
|
||||
|
||||
For example::
|
||||
|
||||
- |
|
||||
This entry has two paragraphs.
|
||||
|
||||
This is the second.
|
@@ -1,25 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
|
||||
import pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo(
|
||||
'reno').version_string()
|
||||
|
||||
# Configure a null logger so that if reno is used as a library by an
|
||||
# application that does not configure logging there are no warnings.
|
||||
logging.getLogger(__name__).addHandler(logging.NullHandler())
|
107
reno/cache.py
107
reno/cache.py
@@ -1,107 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
from reno import loader
|
||||
from reno import scanner
|
||||
|
||||
|
||||
def build_cache_db(conf, versions_to_include):
|
||||
s = scanner.Scanner(conf)
|
||||
notes = s.get_notes_by_version()
|
||||
|
||||
# Default to including all versions returned by the scanner.
|
||||
if not versions_to_include:
|
||||
versions_to_include = list(notes.keys())
|
||||
|
||||
# Build a cache data structure including the file contents as well
|
||||
# as the basic data returned by the scanner.
|
||||
file_contents = {}
|
||||
for version in versions_to_include:
|
||||
for filename, sha in notes[version]:
|
||||
body = s.get_file_at_commit(filename, sha)
|
||||
# We want to save the contents of the file, which is YAML,
|
||||
# inside another YAML file. That looks terribly ugly with
|
||||
# all of the escapes needed to format it properly as
|
||||
# embedded YAML, so parse the input and convert it to a
|
||||
# data structure that can be serialized cleanly.
|
||||
y = yaml.safe_load(body)
|
||||
file_contents[filename] = y
|
||||
|
||||
cache = {
|
||||
'notes': [
|
||||
{'version': k, 'files': v}
|
||||
for k, v in notes.items()
|
||||
],
|
||||
'file-contents': file_contents,
|
||||
}
|
||||
return cache
|
||||
|
||||
|
||||
def write_cache_db(conf, versions_to_include,
|
||||
outfilename=None):
|
||||
"""Create a cache database file for the release notes data.
|
||||
|
||||
Build the cache database from scanning the project history and
|
||||
write it to a file within the project.
|
||||
|
||||
By default, the data is written to the same file the scanner will
|
||||
try to read when it cannot look at the git history. If outfilename
|
||||
is given and is '-' the data is written to stdout
|
||||
instead. Otherwise, if outfilename is given, the data overwrites
|
||||
the named file.
|
||||
|
||||
Return the name of the file created, if any.
|
||||
|
||||
"""
|
||||
if outfilename == '-':
|
||||
stream = sys.stdout
|
||||
close_stream = False
|
||||
elif outfilename:
|
||||
stream = open(outfilename, 'w')
|
||||
close_stream = True
|
||||
else:
|
||||
outfilename = loader.get_cache_filename(conf)
|
||||
if not os.path.exists(os.path.dirname(outfilename)):
|
||||
os.makedirs(os.path.dirname(outfilename))
|
||||
stream = open(outfilename, 'w')
|
||||
close_stream = True
|
||||
try:
|
||||
cache = build_cache_db(
|
||||
conf,
|
||||
versions_to_include=versions_to_include,
|
||||
)
|
||||
yaml.safe_dump(
|
||||
cache,
|
||||
stream,
|
||||
allow_unicode=True,
|
||||
explicit_start=True,
|
||||
encoding='utf-8',
|
||||
)
|
||||
finally:
|
||||
if close_stream:
|
||||
stream.close()
|
||||
return outfilename
|
||||
|
||||
|
||||
def cache_cmd(args, conf):
|
||||
"Generates a release notes cache"
|
||||
write_cache_db(
|
||||
conf=conf,
|
||||
versions_to_include=args.version,
|
||||
outfilename=args.output,
|
||||
)
|
||||
return
|
251
reno/config.py
251
reno/config.py
@@ -1,251 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
import yaml
|
||||
|
||||
from reno import defaults
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Config(object):
|
||||
|
||||
_OPTS = {
|
||||
# The notes subdirectory within the relnotesdir where the
|
||||
# notes live.
|
||||
'notesdir': defaults.NOTES_SUBDIR,
|
||||
|
||||
# Should pre-release versions be merged into the final release
|
||||
# of the same number (1.0.0.0a1 notes appear under 1.0.0).
|
||||
'collapse_pre_releases': True,
|
||||
|
||||
# Should the scanner stop at the base of a branch (True) or go
|
||||
# ahead and scan the entire history (False)?
|
||||
'stop_at_branch_base': True,
|
||||
|
||||
# The git branch to scan. Defaults to the "current" branch
|
||||
# checked out.
|
||||
'branch': None,
|
||||
|
||||
# The earliest version to be included. This is usually the
|
||||
# lowest version number, and is meant to be the oldest
|
||||
# version.
|
||||
'earliest_version': None,
|
||||
|
||||
# The template used by reno new to create a note.
|
||||
'template': defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME),
|
||||
|
||||
# The RE pattern used to match the repo tags representing a valid
|
||||
# release version. The pattern is compiled with the verbose and unicode
|
||||
# flags enabled.
|
||||
'release_tag_re': '''
|
||||
((?:[\d.ab]|rc)+) # digits, a, b, and rc cover regular and
|
||||
# pre-releases
|
||||
''',
|
||||
|
||||
# The RE pattern used to check if a valid release version tag is also a
|
||||
# valid pre-release version. The pattern is compiled with the verbose
|
||||
# and unicode flags enabled. The pattern must define a group called
|
||||
# 'pre_release' that matches the pre-release part of the tag and any
|
||||
# separator, e.g for pre-release version '12.0.0.0rc1' the default RE
|
||||
# pattern will identify '.0rc1' as the value of the group
|
||||
# 'pre_release'.
|
||||
'pre_release_tag_re': '''
|
||||
(?P<pre_release>\.\d+(?:[ab]|rc)+\d*)$
|
||||
''',
|
||||
|
||||
# The pattern for names for branches that are relevant when
|
||||
# scanning history to determine where to stop, to find the
|
||||
# "base" of a branch. Other branches are ignored.
|
||||
'branch_name_re': 'stable/.+',
|
||||
|
||||
# The identifiers and names of permitted sections in the
|
||||
# release notes, in the order in which the final report will
|
||||
# be generated. A prelude section will always be automatically
|
||||
# inserted before the first element of this list.
|
||||
'sections': [
|
||||
['features', 'New Features'],
|
||||
['issues', 'Known Issues'],
|
||||
['upgrade', 'Upgrade Notes'],
|
||||
['deprecations', 'Deprecation Notes'],
|
||||
['critical', 'Critical Issues'],
|
||||
['security', 'Security Issues'],
|
||||
['fixes', 'Bug Fixes'],
|
||||
['other', 'Other Notes'],
|
||||
],
|
||||
|
||||
# The name of the prelude section in the note template. This
|
||||
# allows users to rename the section to, for example,
|
||||
# 'release_summary' or 'project_wide_general_announcements',
|
||||
# which is displayed in titlecase in the report after
|
||||
# replacing underscores with spaces.
|
||||
'prelude_section_name': defaults.PRELUDE_SECTION_NAME,
|
||||
|
||||
# When this option is set to True, any merge commits with no
|
||||
# changes and in which the second or later parent is tagged
|
||||
# are considered "null-merges" that bring the tag information
|
||||
# into the current branch but nothing else.
|
||||
#
|
||||
# OpenStack used to use null-merges to bring final release
|
||||
# tags from stable branches back into the master branch. This
|
||||
# confuses the regular traversal because it makes that stable
|
||||
# branch appear to be part of master and/or the later stable
|
||||
# branch. This option allows us to ignore those.
|
||||
'ignore_null_merges': True,
|
||||
|
||||
# Note files to be ignored. It's useful to be able to ignore a
|
||||
# file if it is edited on the wrong branch. Notes should be
|
||||
# specified by their filename or UID. Setting the value in the
|
||||
# configuration file makes it apply to all branches.
|
||||
'ignore_notes': [],
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_default(cls, opt):
|
||||
"Return the default for an option."
|
||||
try:
|
||||
return cls._OPTS[opt]
|
||||
except KeyError:
|
||||
raise ValueError('unknown option name %r' % (opt,))
|
||||
|
||||
def __init__(self, reporoot, relnotesdir=None):
|
||||
"""Instantiate a Config object
|
||||
|
||||
:param str reporoot:
|
||||
The root directory of the repository.
|
||||
:param str relnotesdir:
|
||||
The directory containing release notes. Defaults to
|
||||
'releasenotes'.
|
||||
"""
|
||||
self.reporoot = reporoot
|
||||
if relnotesdir is None:
|
||||
relnotesdir = defaults.RELEASE_NOTES_SUBDIR
|
||||
self.relnotesdir = relnotesdir
|
||||
# Initialize attributes from the defaults.
|
||||
self.override(**self._OPTS)
|
||||
|
||||
self._contents = {}
|
||||
self._load_file()
|
||||
|
||||
def _load_file(self):
|
||||
filenames = [
|
||||
os.path.join(self.reporoot, self.relnotesdir, 'config.yaml'),
|
||||
os.path.join(self.reporoot, 'reno.yaml')]
|
||||
|
||||
for filename in filenames:
|
||||
if os.path.isfile(filename):
|
||||
break
|
||||
else:
|
||||
LOG.info('no configuration file in: %s', ', '.join(filenames))
|
||||
return
|
||||
|
||||
try:
|
||||
with open(filename, 'r') as fd:
|
||||
self._contents = yaml.safe_load(fd)
|
||||
except IOError as err:
|
||||
LOG.warning('did not load config file %s: %s', filename, err)
|
||||
else:
|
||||
self.override(**self._contents)
|
||||
|
||||
def _rename_prelude_section(self, **kwargs):
|
||||
key = 'prelude_section_name'
|
||||
if key in kwargs and kwargs[key] != self._OPTS[key]:
|
||||
new_prelude_name = kwargs[key]
|
||||
|
||||
self.template = defaults.TEMPLATE.format(new_prelude_name)
|
||||
|
||||
def override(self, **kwds):
|
||||
"""Set the values of the named configuration options.
|
||||
|
||||
Take the values of the keyword arguments as the current value
|
||||
of the same option, regardless of whether a value is already
|
||||
present.
|
||||
|
||||
"""
|
||||
# Replace prelude section name if it has been changed.
|
||||
self._rename_prelude_section(**kwds)
|
||||
|
||||
for n, v in kwds.items():
|
||||
if n not in self._OPTS:
|
||||
LOG.warning('ignoring unknown configuration value %r = %r',
|
||||
n, v)
|
||||
else:
|
||||
setattr(self, n, v)
|
||||
|
||||
def override_from_parsed_args(self, parsed_args):
|
||||
"""Set the values of the configuration options from parsed CLI args.
|
||||
|
||||
This method assumes that the DEST values for the command line
|
||||
arguments are named the same as the configuration options.
|
||||
|
||||
"""
|
||||
arg_values = {
|
||||
o: getattr(parsed_args, o)
|
||||
for o in self._OPTS.keys()
|
||||
if hasattr(parsed_args, o)
|
||||
}
|
||||
self.override(**arg_values)
|
||||
|
||||
@property
|
||||
def reporoot(self):
|
||||
return self._reporoot
|
||||
|
||||
# Ensure that the 'reporoot' value always only ends in one '/'.
|
||||
@reporoot.setter
|
||||
def reporoot(self, value):
|
||||
self._reporoot = value.rstrip('/') + '/'
|
||||
|
||||
@property
|
||||
def notespath(self):
|
||||
"""The path in the repo where notes are kept.
|
||||
|
||||
.. important::
|
||||
|
||||
This does not take ``reporoot`` into account. You need to add this
|
||||
manually if required.
|
||||
"""
|
||||
return os.path.join(self.relnotesdir, self.notesdir)
|
||||
|
||||
@property
|
||||
def options(self):
|
||||
"""Get all configuration options as a dict.
|
||||
|
||||
Returns the actual configuration options after overrides.
|
||||
"""
|
||||
options = {o: getattr(self, o) for o in self._OPTS}
|
||||
return options
|
||||
|
||||
# def parse_config_into(parsed_arguments):
|
||||
|
||||
# """Parse the user config onto the namespace arguments.
|
||||
|
||||
# :param parsed_arguments:
|
||||
# The result of calling :meth:`argparse.ArgumentParser.parse_args`.
|
||||
# :type parsed_arguments:
|
||||
# argparse.Namespace
|
||||
# """
|
||||
# config_path = get_config_path(parsed_arguments.relnotesdir)
|
||||
# config_values = read_config(config_path)
|
||||
|
||||
# for key in config_values.keys():
|
||||
# try:
|
||||
# getattr(parsed_arguments, key)
|
||||
# except AttributeError:
|
||||
# LOG.info('Option "%s" does not apply to this particular command.'
|
||||
# '. Ignoring...', key)
|
||||
# continue
|
||||
# setattr(parsed_arguments, key, config_values[key])
|
||||
|
||||
# parsed_arguments._config = config_values
|
@@ -1,65 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from reno import utils
|
||||
|
||||
|
||||
def _pick_note_file_name(notesdir, slug):
|
||||
"Pick a unique name in notesdir."
|
||||
for i in range(50):
|
||||
newid = utils.get_random_string()
|
||||
notefilename = os.path.join(notesdir, '%s-%s.yaml' % (slug, newid))
|
||||
if not os.path.exists(notefilename):
|
||||
return notefilename
|
||||
else:
|
||||
raise ValueError(
|
||||
'Unable to generate unique random filename '
|
||||
'in %s after 50 tries' % notesdir,
|
||||
)
|
||||
|
||||
|
||||
def _make_note_file(filename, template):
|
||||
notesdir = os.path.dirname(filename)
|
||||
if not os.path.exists(notesdir):
|
||||
os.makedirs(notesdir)
|
||||
with open(filename, 'w') as f:
|
||||
f.write(template)
|
||||
|
||||
|
||||
def _edit_file(filename):
|
||||
if 'EDITOR' not in os.environ:
|
||||
return False
|
||||
subprocess.call([os.environ['EDITOR'], filename])
|
||||
return True
|
||||
|
||||
|
||||
def create_cmd(args, conf):
|
||||
"Create a new release note file from the template."
|
||||
# NOTE(dhellmann): There is a short race window where we might try
|
||||
# to pick a name that does not exist, then overwrite the file if
|
||||
# it is created before we try to write it. This isn't a problem
|
||||
# because this command is expected to be run by one developer in
|
||||
# their local git tree, and so there should not be any concurrency
|
||||
# concern.
|
||||
slug = args.slug.replace(' ', '-')
|
||||
filename = _pick_note_file_name(conf.notespath, slug)
|
||||
_make_note_file(filename, conf.template)
|
||||
if args.edit and not _edit_file(filename):
|
||||
print('Was unable to edit the new note. EDITOR environment variable '
|
||||
'is missing!')
|
||||
print('Created new notes file in %s' % filename)
|
||||
return
|
@@ -1,83 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
RELEASE_NOTES_SUBDIR = 'releasenotes'
|
||||
NOTES_SUBDIR = 'notes'
|
||||
PRELUDE_SECTION_NAME = 'prelude'
|
||||
# This is a format string, so it needs to be formatted wherever it is used.
|
||||
TEMPLATE = """\
|
||||
---
|
||||
{0}: >
|
||||
Replace this text with content to appear at the top of the section for this
|
||||
release. All of the prelude content is merged together and then rendered
|
||||
separately from the items listed in other parts of the file, so the text
|
||||
needs to be worded so that both the prelude and the other items make sense
|
||||
when read independently. This may mean repeating some details. Not every
|
||||
release note requires a prelude. Usually only notes describing major
|
||||
features or adding release theme details should have a prelude.
|
||||
features:
|
||||
- |
|
||||
List new features here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
issues:
|
||||
- |
|
||||
List known issues here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
upgrade:
|
||||
- |
|
||||
List upgrade notes here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
deprecations:
|
||||
- |
|
||||
List deprecations notes here, or remove this section. All of the list
|
||||
items in this section are combined when the release notes are rendered, so
|
||||
the text needs to be worded so that it does not depend on any information
|
||||
only available in another section, such as the prelude. This may mean
|
||||
repeating some details.
|
||||
critical:
|
||||
- |
|
||||
Add critical notes here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
security:
|
||||
- |
|
||||
Add security notes here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
fixes:
|
||||
- |
|
||||
Add normal bug fixes here, or remove this section. All of the list items
|
||||
in this section are combined when the release notes are rendered, so the
|
||||
text needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
other:
|
||||
- |
|
||||
Add other notes here, or remove this section. All of the list items in
|
||||
this section are combined when the release notes are rendered, so the text
|
||||
needs to be worded so that it does not depend on any information only
|
||||
available in another section, such as the prelude. This may mean repeating
|
||||
some details.
|
||||
"""
|
@@ -1,83 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
def _indent_for_list(text, prefix=' '):
|
||||
"""Indent some text to make it work as a list entry.
|
||||
|
||||
Indent all lines except the first with the prefix.
|
||||
"""
|
||||
lines = text.splitlines()
|
||||
return '\n'.join([lines[0]] + [
|
||||
prefix + l
|
||||
for l in lines[1:]
|
||||
]) + '\n'
|
||||
|
||||
|
||||
def format_report(loader, config, versions_to_include, title=None,
|
||||
show_source=True):
|
||||
report = []
|
||||
if title:
|
||||
report.append('=' * len(title))
|
||||
report.append(title)
|
||||
report.append('=' * len(title))
|
||||
report.append('')
|
||||
|
||||
# Read all of the notes files.
|
||||
file_contents = {}
|
||||
for version in versions_to_include:
|
||||
for filename, sha in loader[version]:
|
||||
body = loader.parse_note_file(filename, sha)
|
||||
file_contents[filename] = body
|
||||
|
||||
for version in versions_to_include:
|
||||
report.append(version)
|
||||
report.append('=' * len(version))
|
||||
report.append('')
|
||||
|
||||
# Add the preludes.
|
||||
notefiles = loader[version]
|
||||
prelude_name = config.prelude_section_name
|
||||
notefiles_with_prelude = [(n, sha) for n, sha in notefiles
|
||||
if prelude_name in file_contents[n]]
|
||||
if notefiles_with_prelude:
|
||||
report.append(prelude_name.replace('_', ' ').title())
|
||||
report.append('-' * len(prelude_name))
|
||||
report.append('')
|
||||
|
||||
for n, sha in notefiles_with_prelude:
|
||||
if show_source:
|
||||
report.append('.. %s @ %s\n' % (n, sha))
|
||||
report.append(file_contents[n][prelude_name])
|
||||
report.append('')
|
||||
|
||||
# Add other sections.
|
||||
for section_name, section_title in config.sections:
|
||||
notes = [
|
||||
(n, fn, sha)
|
||||
for fn, sha in notefiles
|
||||
if file_contents[fn].get(section_name)
|
||||
for n in file_contents[fn].get(section_name, [])
|
||||
]
|
||||
if notes:
|
||||
report.append(section_title)
|
||||
report.append('-' * len(section_title))
|
||||
report.append('')
|
||||
for n, fn, sha in notes:
|
||||
if show_source:
|
||||
report.append('.. %s @ %s\n' % (fn, sha))
|
||||
report.append('- %s' % _indent_for_list(n))
|
||||
report.append('')
|
||||
|
||||
return '\n'.join(report)
|
@@ -1,54 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import glob
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from reno import loader
|
||||
from reno import scanner
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def lint_cmd(args, conf):
|
||||
"Check some common mistakes"
|
||||
LOG.debug('starting lint')
|
||||
notesdir = os.path.join(conf.reporoot, conf.notespath)
|
||||
notes = glob.glob(os.path.join(notesdir, '*.yaml'))
|
||||
|
||||
error = 0
|
||||
load = loader.Loader(conf, ignore_cache=True)
|
||||
allowed_section_names = [conf.prelude_section_name] + \
|
||||
[s[0] for s in conf.sections]
|
||||
|
||||
uids = {}
|
||||
for f in notes:
|
||||
LOG.debug('examining %s', f)
|
||||
uid = scanner._get_unique_id(f)
|
||||
uids.setdefault(uid, []).append(f)
|
||||
|
||||
content = load.parse_note_file(f, None)
|
||||
for section_name in content.keys():
|
||||
if section_name not in allowed_section_names:
|
||||
LOG.warning('unrecognized section name %s in %s',
|
||||
section_name, f)
|
||||
error = 1
|
||||
|
||||
for uid, names in sorted(uids.items()):
|
||||
if len(names) > 1:
|
||||
LOG.warning('UID collision: %s', names)
|
||||
error = 1
|
||||
|
||||
return error
|
@@ -1,38 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import logging
|
||||
|
||||
from reno import loader
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def list_cmd(args, conf):
|
||||
"List notes files based on query arguments"
|
||||
LOG.debug('starting list')
|
||||
reporoot = conf.reporoot
|
||||
ldr = loader.Loader(conf)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
versions = ldr.versions
|
||||
for version in versions:
|
||||
notefiles = ldr[version]
|
||||
print(version)
|
||||
for n, sha in notefiles:
|
||||
if n.startswith(reporoot):
|
||||
n = n[len(reporoot):]
|
||||
print('\t%s (%s)' % (n, sha))
|
||||
return
|
140
reno/loader.py
140
reno/loader.py
@@ -1,140 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from reno import scanner
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_cache_filename(conf):
|
||||
return os.path.normpath(os.path.join(
|
||||
conf.reporoot, conf.notespath, 'reno.cache'))
|
||||
|
||||
|
||||
class Loader(object):
|
||||
"Load the release notes for a given repository."
|
||||
|
||||
def __init__(self, conf,
|
||||
ignore_cache=False):
|
||||
"""Initialize a Loader.
|
||||
|
||||
The versions are presented in reverse chronological order.
|
||||
|
||||
Notes files are associated with the earliest version for which
|
||||
they were available, regardless of whether they changed later.
|
||||
|
||||
:param conf: Parsed configuration from file
|
||||
:type conf: reno.config.Config
|
||||
:param ignore_cache: Do not load a cache file if it is present.
|
||||
:type ignore_cache: bool
|
||||
"""
|
||||
self._config = conf
|
||||
self._ignore_cache = ignore_cache
|
||||
|
||||
self._reporoot = conf.reporoot
|
||||
self._notespath = conf.notespath
|
||||
self._branch = conf.branch
|
||||
self._collapse_pre_releases = conf.collapse_pre_releases
|
||||
self._earliest_version = conf.earliest_version
|
||||
|
||||
self._cache = None
|
||||
self._scanner = None
|
||||
self._scanner_output = None
|
||||
self._cache_filename = get_cache_filename(conf)
|
||||
|
||||
self._load_data()
|
||||
|
||||
def _load_data(self):
|
||||
cache_file_exists = os.path.exists(self._cache_filename)
|
||||
|
||||
if self._ignore_cache and cache_file_exists:
|
||||
LOG.debug('ignoring cache file %s', self._cache_filename)
|
||||
|
||||
if (not self._ignore_cache) and cache_file_exists:
|
||||
with open(self._cache_filename, 'r') as f:
|
||||
self._cache = yaml.safe_load(f.read())
|
||||
# Save the cached scanner output to the same attribute
|
||||
# it would be in if we had loaded it "live". This
|
||||
# simplifies some of the logic in the other methods.
|
||||
self._scanner_output = {
|
||||
n['version']: n['files']
|
||||
for n in self._cache['notes']
|
||||
}
|
||||
else:
|
||||
self._scanner = scanner.Scanner(self._config)
|
||||
self._scanner_output = self._scanner.get_notes_by_version()
|
||||
|
||||
@property
|
||||
def versions(self):
|
||||
"A list of all of the versions found."
|
||||
return list(self._scanner_output.keys())
|
||||
|
||||
def __getitem__(self, version):
|
||||
"Return data about the files that should go into a given version."
|
||||
return self._scanner_output[version]
|
||||
|
||||
def parse_note_file(self, filename, sha):
|
||||
"""Return the data structure encoded in the note file.
|
||||
|
||||
Emit warnings for content that does not look valid in some
|
||||
way, but return it anyway for backwards-compatibility.
|
||||
|
||||
"""
|
||||
if self._cache:
|
||||
content = self._cache['file-contents'][filename]
|
||||
else:
|
||||
body = self._scanner.get_file_at_commit(filename, sha)
|
||||
content = yaml.safe_load(body)
|
||||
|
||||
cleaned_content = {}
|
||||
|
||||
for section_name, section_content in content.items():
|
||||
if section_name == self._config.prelude_section_name:
|
||||
if not isinstance(section_content, six.string_types):
|
||||
LOG.warning(
|
||||
('The %s section of %s '
|
||||
'does not parse as a single string. '
|
||||
'Is the YAML input escaped properly?') %
|
||||
(self._config.prelude_section_name, filename),
|
||||
)
|
||||
else:
|
||||
if isinstance(section_content, six.string_types):
|
||||
# A single string is OK, but wrap it with a list
|
||||
# so the rest of the code can treat the data model
|
||||
# consistently.
|
||||
section_content = [section_content]
|
||||
elif not isinstance(section_content, list):
|
||||
LOG.warning(
|
||||
('The %s section of %s '
|
||||
'does not parse as a string or list of strings. '
|
||||
'Is the YAML input escaped properly?') % (
|
||||
section_name, filename),
|
||||
)
|
||||
else:
|
||||
for item in section_content:
|
||||
if not isinstance(item, six.string_types):
|
||||
LOG.warning(
|
||||
('The item %r in the %s section of %s '
|
||||
'parses as a %s instead of a string. '
|
||||
'Is the YAML input escaped properly?'
|
||||
) % (item, section_name,
|
||||
filename, type(item)),
|
||||
)
|
||||
cleaned_content[section_name] = section_content
|
||||
|
||||
return cleaned_content
|
198
reno/main.py
198
reno/main.py
@@ -1,198 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
|
||||
from reno import cache
|
||||
from reno import config
|
||||
from reno import create
|
||||
from reno import defaults
|
||||
from reno import linter
|
||||
from reno import lister
|
||||
from reno import report
|
||||
|
||||
_query_args = [
|
||||
(('--version',),
|
||||
dict(default=[],
|
||||
action='append',
|
||||
help='the version(s) to include, defaults to all')),
|
||||
(('--branch',),
|
||||
dict(default=config.Config.get_default('branch'),
|
||||
help='the branch to scan, defaults to the current')),
|
||||
(('--collapse-pre-releases',),
|
||||
dict(action='store_true',
|
||||
default=config.Config.get_default('collapse_pre_releases'),
|
||||
help='combine pre-releases with their final release')),
|
||||
(('--no-collapse-pre-releases',),
|
||||
dict(action='store_false',
|
||||
dest='collapse_pre_releases',
|
||||
help='show pre-releases separately')),
|
||||
(('--earliest-version',),
|
||||
dict(default=None,
|
||||
help='stop when this version is reached in the history')),
|
||||
(('--ignore-cache',),
|
||||
dict(default=False,
|
||||
action='store_true',
|
||||
help='if there is a cache file present, do not use it')),
|
||||
(('--stop-at-branch-base',),
|
||||
dict(action='store_true',
|
||||
default=True,
|
||||
dest='stop_at_branch_base',
|
||||
help='stop scanning when the branch meets master')),
|
||||
(('--no-stop-at-branch-base',),
|
||||
dict(action='store_false',
|
||||
dest='stop_at_branch_base',
|
||||
help='do not stop scanning when the branch meets master')),
|
||||
]
|
||||
|
||||
|
||||
def _build_query_arg_group(parser):
|
||||
group = parser.add_argument_group('query')
|
||||
for args, kwds in _query_args:
|
||||
group.add_argument(*args, **kwds)
|
||||
|
||||
|
||||
def main(argv=sys.argv[1:]):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-v', '--verbose',
|
||||
dest='verbosity',
|
||||
default=logging.INFO,
|
||||
help='produce more output',
|
||||
action='store_const',
|
||||
const=logging.DEBUG,
|
||||
)
|
||||
parser.add_argument(
|
||||
'-q', '--quiet',
|
||||
dest='verbosity',
|
||||
action='store_const',
|
||||
const=logging.WARN,
|
||||
help='produce less output',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--rel-notes-dir', '-d',
|
||||
dest='relnotesdir',
|
||||
default=defaults.RELEASE_NOTES_SUBDIR,
|
||||
help='location of release notes YAML files',
|
||||
)
|
||||
subparsers = parser.add_subparsers(
|
||||
title='commands',
|
||||
)
|
||||
|
||||
do_new = subparsers.add_parser(
|
||||
'new',
|
||||
help='create a new note',
|
||||
)
|
||||
do_new.add_argument(
|
||||
'--edit',
|
||||
action='store_true',
|
||||
help='Edit note after its creation (require EDITOR env variable)',
|
||||
)
|
||||
do_new.add_argument(
|
||||
'slug',
|
||||
help='descriptive title of note (keep it short)',
|
||||
)
|
||||
do_new.add_argument(
|
||||
'reporoot',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='root of the git repository',
|
||||
)
|
||||
do_new.set_defaults(func=create.create_cmd)
|
||||
|
||||
do_list = subparsers.add_parser(
|
||||
'list',
|
||||
help='list notes files based on query arguments',
|
||||
)
|
||||
_build_query_arg_group(do_list)
|
||||
do_list.add_argument(
|
||||
'reporoot',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='root of the git repository',
|
||||
)
|
||||
do_list.set_defaults(func=lister.list_cmd)
|
||||
|
||||
do_report = subparsers.add_parser(
|
||||
'report',
|
||||
help='generate release notes report',
|
||||
)
|
||||
do_report.add_argument(
|
||||
'reporoot',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='root of the git repository',
|
||||
)
|
||||
do_report.add_argument(
|
||||
'--output', '-o',
|
||||
default=None,
|
||||
help='output filename, defaults to stdout',
|
||||
)
|
||||
do_report.add_argument(
|
||||
'--no-show-source',
|
||||
dest='show_source',
|
||||
default=True,
|
||||
action='store_false',
|
||||
help='do not show the source for notes',
|
||||
)
|
||||
do_report.add_argument(
|
||||
'--title',
|
||||
default='Release Notes',
|
||||
help='set the main title of the generated report',
|
||||
)
|
||||
_build_query_arg_group(do_report)
|
||||
do_report.set_defaults(func=report.report_cmd)
|
||||
|
||||
do_cache = subparsers.add_parser(
|
||||
'cache',
|
||||
help='generate release notes cache',
|
||||
)
|
||||
do_cache.add_argument(
|
||||
'reporoot',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='root of the git repository',
|
||||
)
|
||||
do_cache.add_argument(
|
||||
'--output', '-o',
|
||||
default=None,
|
||||
help=('output filename, '
|
||||
'defaults to the cache file within the notesdir, '
|
||||
'use "-" for stdout'),
|
||||
)
|
||||
_build_query_arg_group(do_cache)
|
||||
do_cache.set_defaults(func=cache.cache_cmd)
|
||||
|
||||
do_linter = subparsers.add_parser(
|
||||
'lint',
|
||||
help='check some common mistakes',
|
||||
)
|
||||
do_linter.add_argument(
|
||||
'reporoot',
|
||||
default='.',
|
||||
nargs='?',
|
||||
help='root of the git repository',
|
||||
)
|
||||
do_linter.set_defaults(func=linter.lint_cmd)
|
||||
|
||||
args = parser.parse_args(argv)
|
||||
conf = config.Config(args.reporoot, args.relnotesdir)
|
||||
conf.override_from_parsed_args(args)
|
||||
|
||||
logging.basicConfig(
|
||||
level=args.verbosity,
|
||||
format='%(message)s',
|
||||
)
|
||||
|
||||
return args.func(args, conf)
|
@@ -1,38 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from reno import formatter
|
||||
from reno import loader
|
||||
|
||||
|
||||
def report_cmd(args, conf):
|
||||
"Generates a release notes report"
|
||||
ldr = loader.Loader(conf)
|
||||
if args.version:
|
||||
versions = args.version
|
||||
else:
|
||||
versions = ldr.versions
|
||||
text = formatter.format_report(
|
||||
ldr,
|
||||
conf,
|
||||
versions,
|
||||
title=args.title,
|
||||
show_source=args.show_source,
|
||||
)
|
||||
if args.output:
|
||||
with open(args.output, 'w') as f:
|
||||
f.write(text)
|
||||
else:
|
||||
print(text)
|
||||
return
|
1196
reno/scanner.py
1196
reno/scanner.py
File diff suppressed because it is too large
Load Diff
@@ -1,123 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
|
||||
from docutils import nodes
|
||||
from docutils.parsers import rst
|
||||
from docutils.parsers.rst import directives
|
||||
from docutils import statemachine
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
|
||||
from dulwich import repo
|
||||
from reno import config
|
||||
from reno import defaults
|
||||
from reno import formatter
|
||||
from reno import loader
|
||||
|
||||
|
||||
class ReleaseNotesDirective(rst.Directive):
|
||||
|
||||
has_content = True
|
||||
|
||||
option_spec = {
|
||||
'branch': directives.unchanged,
|
||||
'reporoot': directives.unchanged,
|
||||
'relnotessubdir': directives.unchanged,
|
||||
'notesdir': directives.unchanged,
|
||||
'version': directives.unchanged,
|
||||
'collapse-pre-releases': directives.flag,
|
||||
'earliest-version': directives.unchanged,
|
||||
'stop-at-branch-base': directives.flag,
|
||||
'ignore-notes': directives.unchanged,
|
||||
}
|
||||
|
||||
def run(self):
|
||||
env = self.state.document.settings.env
|
||||
app = env.app
|
||||
|
||||
def info(msg):
|
||||
app.info('[reno] %s' % (msg,))
|
||||
|
||||
title = ' '.join(self.content)
|
||||
branch = self.options.get('branch')
|
||||
reporoot_opt = self.options.get('reporoot', '.')
|
||||
reporoot = os.path.abspath(reporoot_opt)
|
||||
# When building on RTD.org the root directory may not be
|
||||
# the current directory, so look for it.
|
||||
reporoot = repo.Repo.discover(reporoot).path
|
||||
relnotessubdir = self.options.get('relnotessubdir',
|
||||
defaults.RELEASE_NOTES_SUBDIR)
|
||||
ignore_notes = [
|
||||
name.strip()
|
||||
for name in self.options.get('ignore-notes', '').split(',')
|
||||
]
|
||||
conf = config.Config(reporoot, relnotessubdir)
|
||||
opt_overrides = {}
|
||||
if 'notesdir' in self.options:
|
||||
opt_overrides['notesdir'] = self.options.get('notesdir')
|
||||
version_opt = self.options.get('version')
|
||||
# FIXME(dhellmann): Force these flags True for now and figure
|
||||
# out how Sphinx passes a "false" flag later.
|
||||
# 'collapse-pre-releases' in self.options
|
||||
opt_overrides['collapse_pre_releases'] = True
|
||||
# Only stop at the branch base if we have not been told
|
||||
# explicitly which versions to include.
|
||||
opt_overrides['stop_at_branch_base'] = (version_opt is None)
|
||||
if 'earliest-version' in self.options:
|
||||
opt_overrides['earliest_version'] = self.options.get(
|
||||
'earliest-version')
|
||||
if branch:
|
||||
opt_overrides['branch'] = branch
|
||||
if ignore_notes:
|
||||
opt_overrides['ignore_notes'] = ignore_notes
|
||||
conf.override(**opt_overrides)
|
||||
|
||||
notesdir = os.path.join(relnotessubdir, conf.notesdir)
|
||||
info('scanning %s for %s release notes' %
|
||||
(os.path.join(conf.reporoot, notesdir),
|
||||
branch or 'current branch'))
|
||||
|
||||
ldr = loader.Loader(conf)
|
||||
if version_opt is not None:
|
||||
versions = [
|
||||
v.strip()
|
||||
for v in version_opt.split(',')
|
||||
]
|
||||
else:
|
||||
versions = ldr.versions
|
||||
info('got versions %s' % (versions,))
|
||||
text = formatter.format_report(
|
||||
ldr,
|
||||
conf,
|
||||
versions,
|
||||
title=title,
|
||||
)
|
||||
source_name = '<%s %s>' % (__name__, branch or 'current branch')
|
||||
result = statemachine.ViewList()
|
||||
for line in text.splitlines():
|
||||
result.append(line, source_name)
|
||||
|
||||
node = nodes.section()
|
||||
node.document = self.state.document
|
||||
nested_parse_with_titles(self.state, result, node)
|
||||
return node.children
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_directive('release-notes', ReleaseNotesDirective)
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='[%(name)s] %(message)s',
|
||||
)
|
@@ -1,33 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2010-2011 OpenStack Foundation
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import testtools
|
||||
|
||||
|
||||
class TestCase(testtools.TestCase):
|
||||
|
||||
"""Test case base class for all unit tests."""
|
||||
|
||||
def setUp(self):
|
||||
super(TestCase, self).setUp()
|
||||
self._stdout_fixture = fixtures.StringStream('stdout')
|
||||
self.stdout = self.useFixture(self._stdout_fixture).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.stdout))
|
||||
self._stderr_fixture = fixtures.StringStream('stderr')
|
||||
self.stderr = self.useFixture(self._stderr_fixture).stream
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', self.stderr))
|
@@ -1,86 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import textwrap
|
||||
|
||||
import mock
|
||||
|
||||
from reno import cache
|
||||
from reno import config
|
||||
from reno.tests import base
|
||||
|
||||
|
||||
class TestCache(base.TestCase):
|
||||
|
||||
scanner_output = {
|
||||
'0.0.0': [('note1', 'shaA')],
|
||||
'1.0.0': [('note2', 'shaB'), ('note3', 'shaC')],
|
||||
}
|
||||
|
||||
note_bodies = {
|
||||
'note1': textwrap.dedent("""
|
||||
prelude: >
|
||||
This is the prelude.
|
||||
"""),
|
||||
'note2': textwrap.dedent("""
|
||||
issues:
|
||||
- This is the first issue.
|
||||
- This is the second issue.
|
||||
"""),
|
||||
'note3': textwrap.dedent("""
|
||||
features:
|
||||
- We added a feature!
|
||||
""")
|
||||
}
|
||||
|
||||
def _get_note_body(self, filename, sha):
|
||||
return self.note_bodies.get(filename, '')
|
||||
|
||||
def setUp(self):
|
||||
super(TestCache, self).setUp()
|
||||
self.useFixture(
|
||||
fixtures.MockPatch('reno.scanner.Scanner.get_file_at_commit',
|
||||
new=self._get_note_body)
|
||||
)
|
||||
self.c = config.Config('.')
|
||||
|
||||
def test_build_cache_db(self):
|
||||
with mock.patch('reno.scanner.Scanner.get_notes_by_version') as gnbv:
|
||||
gnbv.return_value = self.scanner_output
|
||||
db = cache.build_cache_db(
|
||||
self.c,
|
||||
versions_to_include=[],
|
||||
)
|
||||
expected = {
|
||||
'notes': [
|
||||
{'version': k, 'files': v}
|
||||
for k, v in self.scanner_output.items()
|
||||
],
|
||||
'file-contents': {
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.\n',
|
||||
},
|
||||
'note2': {
|
||||
'issues': [
|
||||
'This is the first issue.',
|
||||
'This is the second issue.',
|
||||
],
|
||||
},
|
||||
'note3': {
|
||||
'features': ['We added a feature!'],
|
||||
},
|
||||
},
|
||||
}
|
||||
self.assertEqual(expected, db)
|
@@ -1,174 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
import argparse
|
||||
import os
|
||||
|
||||
import fixtures
|
||||
|
||||
from reno import config
|
||||
from reno import defaults
|
||||
from reno import main
|
||||
from reno.tests import base
|
||||
|
||||
import mock
|
||||
|
||||
|
||||
class TestConfig(base.TestCase):
|
||||
EXAMPLE_CONFIG = """
|
||||
collapse_pre_releases: false
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfig, self).setUp()
|
||||
# Temporary directory to store our config
|
||||
self.tempdir = self.useFixture(fixtures.TempDir())
|
||||
|
||||
def test_defaults(self):
|
||||
c = config.Config(self.tempdir.path)
|
||||
actual = c.options
|
||||
self.assertEqual(config.Config._OPTS, actual)
|
||||
|
||||
def test_override(self):
|
||||
c = config.Config(self.tempdir.path)
|
||||
c.override(
|
||||
collapse_pre_releases=False,
|
||||
)
|
||||
actual = c.options
|
||||
expected = {}
|
||||
expected.update(config.Config._OPTS)
|
||||
expected['collapse_pre_releases'] = False
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_override_multiple(self):
|
||||
c = config.Config(self.tempdir.path)
|
||||
c.override(
|
||||
notesdir='value1',
|
||||
)
|
||||
c.override(
|
||||
notesdir='value2',
|
||||
)
|
||||
actual = c.options
|
||||
expected = {}
|
||||
expected.update(config.Config._OPTS)
|
||||
expected['notesdir'] = 'value2'
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_load_file_not_present(self):
|
||||
with mock.patch.object(config.LOG, 'info') as logger:
|
||||
config.Config(self.tempdir.path)
|
||||
self.assertEqual(1, logger.call_count)
|
||||
|
||||
def _test_load_file(self, config_path):
|
||||
with open(config_path, 'w') as fd:
|
||||
fd.write(self.EXAMPLE_CONFIG)
|
||||
self.addCleanup(os.unlink, config_path)
|
||||
c = config.Config(self.tempdir.path)
|
||||
self.assertEqual(False, c.collapse_pre_releases)
|
||||
|
||||
def test_load_file_in_releasenotesdir(self):
|
||||
rn_path = self.tempdir.join('releasenotes')
|
||||
os.mkdir(rn_path)
|
||||
config_path = self.tempdir.join('releasenotes/config.yaml')
|
||||
self._test_load_file(config_path)
|
||||
|
||||
def test_load_file_in_repodir(self):
|
||||
config_path = self.tempdir.join('reno.yaml')
|
||||
self._test_load_file(config_path)
|
||||
|
||||
def test_get_default(self):
|
||||
d = config.Config.get_default('notesdir')
|
||||
self.assertEqual('notes', d)
|
||||
|
||||
def test_get_default_unknown(self):
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
config.Config.get_default,
|
||||
'unknownopt',
|
||||
)
|
||||
|
||||
def _run_override_from_parsed_args(self, argv):
|
||||
parser = argparse.ArgumentParser()
|
||||
main._build_query_arg_group(parser)
|
||||
args = parser.parse_args(argv)
|
||||
c = config.Config(self.tempdir.path)
|
||||
c.override_from_parsed_args(args)
|
||||
return c
|
||||
|
||||
def test_override_from_parsed_args_empty(self):
|
||||
c = self._run_override_from_parsed_args([])
|
||||
actual = {
|
||||
o: getattr(c, o)
|
||||
for o in config.Config._OPTS.keys()
|
||||
}
|
||||
self.assertEqual(config.Config._OPTS, actual)
|
||||
|
||||
def test_override_from_parsed_args(self):
|
||||
c = self._run_override_from_parsed_args([
|
||||
'--no-collapse-pre-releases',
|
||||
])
|
||||
actual = c.options
|
||||
expected = {}
|
||||
expected.update(config.Config._OPTS)
|
||||
expected['collapse_pre_releases'] = False
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
def test_override_from_parsed_args_ignore_non_options(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
main._build_query_arg_group(parser)
|
||||
parser.add_argument('not_a_config_option')
|
||||
args = parser.parse_args(['value'])
|
||||
c = config.Config(self.tempdir.path)
|
||||
c.override_from_parsed_args(args)
|
||||
self.assertFalse(hasattr(c, 'not_a_config_option'))
|
||||
|
||||
|
||||
class TestConfigProperties(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestConfigProperties, self).setUp()
|
||||
# Temporary directory to store our config
|
||||
self.tempdir = self.useFixture(fixtures.TempDir())
|
||||
self.c = config.Config('releasenotes')
|
||||
|
||||
def test_reporoot(self):
|
||||
self.c.reporoot = 'blah//'
|
||||
self.assertEqual('blah/', self.c.reporoot)
|
||||
self.c.reporoot = 'blah'
|
||||
self.assertEqual('blah/', self.c.reporoot)
|
||||
|
||||
def test_notespath(self):
|
||||
self.assertEqual('releasenotes/notes', self.c.notespath)
|
||||
self.c.override(notesdir='thenotes')
|
||||
self.assertEqual('releasenotes/thenotes', self.c.notespath)
|
||||
|
||||
def test_template(self):
|
||||
template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
|
||||
self.assertEqual(template, self.c.template)
|
||||
self.c.override(template='i-am-a-template')
|
||||
self.assertEqual('i-am-a-template', self.c.template)
|
||||
|
||||
def test_prelude_override(self):
|
||||
template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
|
||||
self.assertEqual(template, self.c.template)
|
||||
self.c.override(prelude_section_name='fake_prelude_name')
|
||||
expected_template = defaults.TEMPLATE.format('fake_prelude_name')
|
||||
self.assertEqual(expected_template, self.c.template)
|
||||
|
||||
def test_prelude_and_template_override(self):
|
||||
template = defaults.TEMPLATE.format(defaults.PRELUDE_SECTION_NAME)
|
||||
self.assertEqual(template, self.c.template)
|
||||
self.c.override(prelude_section_name='fake_prelude_name',
|
||||
template='i-am-a-template')
|
||||
self.assertEqual('fake_prelude_name', self.c.prelude_section_name)
|
||||
self.assertEqual('i-am-a-template', self.c.template)
|
@@ -1,65 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
|
||||
from reno import create
|
||||
from reno.tests import base
|
||||
|
||||
|
||||
class TestPickFileName(base.TestCase):
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
def test_not_random_enough(self, exists):
|
||||
exists.return_value = True
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
create._pick_note_file_name,
|
||||
'somepath',
|
||||
'someslug',
|
||||
)
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
def test_random_enough(self, exists):
|
||||
exists.return_value = False
|
||||
result = create._pick_note_file_name('somepath', 'someslug')
|
||||
self.assertIn('somepath', result)
|
||||
self.assertIn('someslug', result)
|
||||
|
||||
|
||||
class TestCreate(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCreate, self).setUp()
|
||||
self.tmpdir = self.useFixture(fixtures.TempDir()).path
|
||||
|
||||
def test_create_from_template(self):
|
||||
filename = create._pick_note_file_name(self.tmpdir, 'theslug')
|
||||
create._make_note_file(filename, 'i-am-a-template')
|
||||
with open(filename, 'r') as f:
|
||||
body = f.read()
|
||||
self.assertEqual('i-am-a-template', body)
|
||||
|
||||
def test_edit(self):
|
||||
self.useFixture(fixtures.EnvironmentVariable('EDITOR', 'myeditor'))
|
||||
with mock.patch('subprocess.call') as call_mock:
|
||||
self.assertTrue(create._edit_file('somepath'))
|
||||
call_mock.assert_called_once_with(['myeditor', 'somepath'])
|
||||
|
||||
def test_edit_without_editor_env_var(self):
|
||||
self.useFixture(fixtures.EnvironmentVariable('EDITOR'))
|
||||
with mock.patch('subprocess.call') as call_mock:
|
||||
self.assertFalse(create._edit_file('somepath'))
|
||||
call_mock.assert_not_called()
|
@@ -1,158 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
|
||||
from reno import config
|
||||
from reno import formatter
|
||||
from reno import loader
|
||||
from reno.tests import base
|
||||
|
||||
|
||||
class TestFormatterBase(base.TestCase):
|
||||
|
||||
scanner_output = {
|
||||
'0.0.0': [('note1', 'shaA')],
|
||||
'1.0.0': [('note2', 'shaB'), ('note3', 'shaC')],
|
||||
}
|
||||
|
||||
versions = ['0.0.0', '1.0.0']
|
||||
|
||||
def _get_note_body(self, reporoot, filename, sha):
|
||||
return self.note_bodies.get(filename, '')
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatterBase, self).setUp()
|
||||
|
||||
def _load(ldr):
|
||||
ldr._scanner_output = self.scanner_output
|
||||
ldr._cache = {
|
||||
'file-contents': self.note_bodies
|
||||
}
|
||||
|
||||
self.c = config.Config('reporoot')
|
||||
|
||||
with mock.patch('reno.loader.Loader._load_data', _load):
|
||||
self.ldr = loader.Loader(
|
||||
self.c,
|
||||
ignore_cache=False,
|
||||
)
|
||||
|
||||
|
||||
class TestFormatter(TestFormatterBase):
|
||||
|
||||
note_bodies = {
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.',
|
||||
},
|
||||
'note2': {
|
||||
'issues': [
|
||||
'This is the first issue.',
|
||||
'This is the second issue.',
|
||||
],
|
||||
},
|
||||
'note3': {
|
||||
'features': [
|
||||
'We added a feature!',
|
||||
],
|
||||
'upgrade': None,
|
||||
},
|
||||
}
|
||||
|
||||
def test_with_title(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
self.assertIn('This is the title', result)
|
||||
|
||||
def test_versions(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title='This is the title',
|
||||
)
|
||||
self.assertIn('0.0.0\n=====', result)
|
||||
self.assertIn('1.0.0\n=====', result)
|
||||
|
||||
def test_without_title(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
self.assertNotIn('This is the title', result)
|
||||
|
||||
def test_default_section_order(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
prelude_pos = result.index('This is the prelude.')
|
||||
issues_pos = result.index('This is the first issue.')
|
||||
features_pos = result.index('We added a feature!')
|
||||
expected = [prelude_pos, features_pos, issues_pos]
|
||||
actual = list(sorted([prelude_pos, features_pos, issues_pos]))
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
|
||||
class TestFormatterCustomSections(TestFormatterBase):
|
||||
note_bodies = {
|
||||
'note1': {
|
||||
'prelude': 'This is the prelude.',
|
||||
},
|
||||
'note2': {
|
||||
'features': [
|
||||
'This is the first feature.',
|
||||
],
|
||||
'api': [
|
||||
'This is the API change for the first feature.',
|
||||
],
|
||||
},
|
||||
'note3': {
|
||||
'api': [
|
||||
'This is the API change for the second feature.',
|
||||
],
|
||||
'features': [
|
||||
'This is the second feature.',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestFormatterCustomSections, self).setUp()
|
||||
self.c.override(sections=[
|
||||
['api', 'API Changes'],
|
||||
['features', 'New Features'],
|
||||
])
|
||||
|
||||
def test_custom_section_order(self):
|
||||
result = formatter.format_report(
|
||||
loader=self.ldr,
|
||||
config=self.c,
|
||||
versions_to_include=self.versions,
|
||||
title=None,
|
||||
)
|
||||
prelude_pos = result.index('This is the prelude.')
|
||||
api_pos = result.index('API Changes')
|
||||
features_pos = result.index('New Features')
|
||||
expected = [prelude_pos, api_pos, features_pos]
|
||||
actual = list(sorted([prelude_pos, features_pos, api_pos]))
|
||||
self.assertEqual(expected, actual)
|
@@ -1,90 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import textwrap
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
import six
|
||||
import yaml
|
||||
|
||||
from reno import config
|
||||
from reno import loader
|
||||
from reno.tests import base
|
||||
|
||||
|
||||
class TestValidate(base.TestCase):
|
||||
|
||||
scanner_output = {
|
||||
'0.0.0': [('note', 'shaA')],
|
||||
}
|
||||
|
||||
versions = ['0.0.0']
|
||||
|
||||
def setUp(self):
|
||||
super(TestValidate, self).setUp()
|
||||
self.logger = self.useFixture(
|
||||
fixtures.FakeLogger(
|
||||
format='%(message)s',
|
||||
level=logging.WARNING,
|
||||
)
|
||||
)
|
||||
self.c = config.Config('reporoot')
|
||||
|
||||
def _make_loader(self, note_bodies):
|
||||
def _load(ldr):
|
||||
ldr._scanner_output = self.scanner_output
|
||||
ldr._cache = {
|
||||
'file-contents': {'note1': note_bodies},
|
||||
}
|
||||
|
||||
with mock.patch('reno.loader.Loader._load_data', _load):
|
||||
return loader.Loader(
|
||||
self.c,
|
||||
ignore_cache=False,
|
||||
)
|
||||
|
||||
def test_prelude_list(self):
|
||||
note_bodies = yaml.safe_load(textwrap.dedent('''
|
||||
prelude:
|
||||
- This is the first comment.
|
||||
- This is a second.
|
||||
'''))
|
||||
self.assertIsInstance(note_bodies['prelude'], list)
|
||||
ldr = self._make_loader(note_bodies)
|
||||
ldr.parse_note_file('note1', None)
|
||||
self.assertIn('prelude', self.logger.output)
|
||||
|
||||
def test_non_prelude_single_string_converted_to_list(self):
|
||||
note_bodies = yaml.safe_load(textwrap.dedent('''
|
||||
issues: |
|
||||
This is a single string.
|
||||
'''))
|
||||
print(type(note_bodies['issues']))
|
||||
self.assertIsInstance(note_bodies['issues'], six.string_types)
|
||||
ldr = self._make_loader(note_bodies)
|
||||
parse_results = ldr.parse_note_file('note1', None)
|
||||
self.assertIsInstance(parse_results['issues'], list)
|
||||
|
||||
def test_note_with_colon_as_dict(self):
|
||||
note_bodies = yaml.safe_load(textwrap.dedent('''
|
||||
issues:
|
||||
- This is the first issue.
|
||||
- dict: This is parsed as a dictionary.
|
||||
'''))
|
||||
self.assertIsInstance(note_bodies['issues'][-1], dict)
|
||||
ldr = self._make_loader(note_bodies)
|
||||
ldr.parse_note_file('note1', None)
|
||||
self.assertIn('dict', self.logger.output)
|
File diff suppressed because it is too large
Load Diff
@@ -1,42 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from reno.tests import base
|
||||
from reno import utils
|
||||
|
||||
|
||||
class TestGetRandomString(base.TestCase):
|
||||
|
||||
@mock.patch('random.randrange')
|
||||
@mock.patch('os.urandom')
|
||||
def test_no_urandom(self, urandom, randrange):
|
||||
urandom.side_effect = Exception('cannot use this')
|
||||
randrange.return_value = ord('a')
|
||||
actual = utils.get_random_string()
|
||||
expected = '61' * 8 # hex for ord('a')
|
||||
self.assertIsInstance(actual, six.text_type)
|
||||
self.assertEqual(expected, actual)
|
||||
|
||||
@mock.patch('random.randrange')
|
||||
@mock.patch('os.urandom')
|
||||
def test_with_urandom(self, urandom, randrange):
|
||||
urandom.return_value = b'\x62' * 8
|
||||
randrange.return_value = ord('a')
|
||||
actual = utils.get_random_string()
|
||||
expected = '62' * 8 # hex for ord('b')
|
||||
self.assertIsInstance(actual, six.text_type)
|
||||
self.assertEqual(expected, actual)
|
@@ -1,54 +0,0 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import binascii
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
import random
|
||||
import subprocess
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_random_string(nbytes=8):
|
||||
"""Return a fixed-length random string
|
||||
|
||||
:rtype: six.text_type
|
||||
"""
|
||||
try:
|
||||
# NOTE(dhellmann): Not all systems support urandom().
|
||||
# hexlify returns six.binary_type, decode to convert to six.text_type.
|
||||
val = binascii.hexlify(os.urandom(nbytes)).decode('utf-8')
|
||||
except Exception as e:
|
||||
print('ERROR, perhaps urandom is not supported: %s' % e)
|
||||
val = u''.join(u'%02x' % random.randrange(256)
|
||||
for i in range(nbytes))
|
||||
return val
|
||||
|
||||
|
||||
def check_output(*args, **kwds):
|
||||
"""Unicode-aware wrapper for subprocess.check_output"""
|
||||
process = subprocess.Popen(stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
*args, **kwds)
|
||||
output, errors = process.communicate()
|
||||
retcode = process.poll()
|
||||
if errors:
|
||||
LOG.debug('ran: %s', ' '.join(*args))
|
||||
LOG.debug('returned: %s', retcode)
|
||||
LOG.debug('error output: %s', errors.rstrip())
|
||||
LOG.debug('regular output: %s', output.rstrip())
|
||||
if retcode:
|
||||
LOG.debug('raising error')
|
||||
raise subprocess.CalledProcessError(retcode, args, output=output)
|
||||
return output.decode('utf-8')
|
@@ -1,8 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
pbr>=1.4
|
||||
PyYAML>=3.1.0
|
||||
six>=1.9.0
|
||||
dulwich>=0.15.0 # Apache-2.0
|
50
setup.cfg
50
setup.cfg
@@ -1,50 +0,0 @@
|
||||
[metadata]
|
||||
name = reno
|
||||
summary = RElease NOtes manager
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack
|
||||
author-email = openstack-dev@lists.openstack.org
|
||||
home-page = https://docs.openstack.org/reno/latest/
|
||||
classifier =
|
||||
Environment :: OpenStack
|
||||
Intended Audience :: Information Technology
|
||||
Intended Audience :: System Administrators
|
||||
License :: OSI Approved :: Apache Software License
|
||||
Operating System :: POSIX :: Linux
|
||||
Programming Language :: Python
|
||||
Programming Language :: Python :: 2
|
||||
Programming Language :: Python :: 2.7
|
||||
Programming Language :: Python :: 3
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
reno
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
reno = reno.main:main
|
||||
|
||||
[extras]
|
||||
sphinx =
|
||||
sphinx>=1.5.1,!=1.6.1 # BSD
|
||||
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
warning-is-error = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[compile_catalog]
|
||||
directory = reno/locale
|
||||
domain = reno
|
||||
|
||||
[update_catalog]
|
||||
domain = reno
|
||||
output_dir = reno/locale
|
||||
input_file = reno/locale/reno.pot
|
29
setup.py
29
setup.py
@@ -1,29 +0,0 @@
|
||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
# implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT
|
||||
import setuptools
|
||||
|
||||
# In python < 2.7.4, a lazy loading of package `pbr` will break
|
||||
# setuptools if some other modules registered functions in `atexit`.
|
||||
# solution from: http://bugs.python.org/issue15881#msg170215
|
||||
try:
|
||||
import multiprocessing # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
setuptools.setup(
|
||||
setup_requires=['pbr'],
|
||||
pbr=True)
|
@@ -1,14 +0,0 @@
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
|
||||
hacking>=0.12.0,!=0.13.0,<0.14 # Apache-2.0
|
||||
|
||||
mock>=1.2
|
||||
|
||||
coverage>=3.6
|
||||
python-subunit>=0.0.18
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
testrepository>=0.0.18
|
||||
testscenarios>=0.4
|
||||
testtools>=1.4.0
|
41
tox.ini
41
tox.ini
@@ -1,41 +0,0 @@
|
||||
[tox]
|
||||
minversion = 1.6
|
||||
envlist = py35,py27,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = pip install -U {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
deps =
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
.[sphinx]
|
||||
commands =
|
||||
python setup.py test --slowest --coverage --coverage-package-name=reno --testr-args='{posargs}'
|
||||
coverage report --show-missing
|
||||
|
||||
[testenv:pep8]
|
||||
commands =
|
||||
flake8
|
||||
reno -q lint
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:cover]
|
||||
commands = python setup.py test --coverage --testr-args='{posargs}'
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:debug]
|
||||
commands = oslo_debug_helper {posargs}
|
||||
|
||||
[flake8]
|
||||
# E123, E125 skipped as they are invalid PEP-8.
|
||||
|
||||
show-source = True
|
||||
ignore = E123,E125
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build
|
Reference in New Issue
Block a user