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