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: Id5c758d0c81166d0f591d95d335d5bfefd77fc5e
This commit is contained in:
parent
c3bf1aa507
commit
6cd93616d3
41
.gitignore
vendored
41
.gitignore
vendored
@ -1,41 +0,0 @@
|
||||
.DS_Store
|
||||
.venv
|
||||
|
||||
# Packages
|
||||
*.py[cod]
|
||||
*.egg
|
||||
/.eggs
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
sdist
|
||||
|
||||
# Unit test / coverage reports
|
||||
.cache
|
||||
.coverage
|
||||
.tox
|
||||
.testrepository
|
||||
|
||||
# pbr generates these
|
||||
AUTHORS
|
||||
ChangeLog
|
||||
|
||||
# Editors
|
||||
*~
|
||||
.*.swp
|
||||
.bak
|
||||
|
||||
# Autohelp
|
||||
autogenerate_config_docs/venv
|
||||
autogenerate_config_docs/sources
|
||||
autogenerate_config_docs/*-conf-changes-*.xml
|
||||
|
||||
# sitemap
|
||||
sitemap/sitemap_docs.openstack.org.xml
|
||||
|
||||
# Files created by releasenotes build
|
||||
releasenotes/build
|
||||
|
||||
# repo pulled down by doc-tools-update-cli-reference
|
||||
openstack-manuals
|
@ -1,4 +0,0 @@
|
||||
[gerrit]
|
||||
host=review.openstack.org
|
||||
port=29418
|
||||
project=openstack/openstack-doc-tools.git
|
4
.mailmap
4
.mailmap
@ -1,4 +0,0 @@
|
||||
# Format is:
|
||||
# <preferred e-mail> <other e-mail 1>
|
||||
# <preferred e-mail> <other e-mail 2>
|
||||
<guoyingc@cn.ibm.com> <daisy.ycguo@gmail.com>
|
@ -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,32 +0,0 @@
|
||||
Our community welcomes all people interested in open source cloud computing,
|
||||
and encourages you to join the `OpenStack Foundation <http://www.openstack.org/join>`_.
|
||||
The best way to get involved with the community is to talk with others online
|
||||
or at a meetup and offer contributions through our processes, the `OpenStack
|
||||
wiki <http://wiki.openstack.org>`_, blogs, or on IRC at ``#openstack``
|
||||
on ``irc.freenode.net``.
|
||||
|
||||
We welcome all types of contributions, from blueprint designs to documentation
|
||||
to testing to deployment scripts.
|
||||
|
||||
If you would like to contribute to the development,
|
||||
you must follow the steps in this page:
|
||||
|
||||
http://docs.openstack.org/infra/manual/developers.html
|
||||
|
||||
Once those steps have been completed, changes to OpenStack
|
||||
should be submitted for review via the Gerrit tool, following
|
||||
the workflow documented at:
|
||||
|
||||
http://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/openstack-manuals
|
||||
|
||||
.. note::
|
||||
|
||||
To be able to run ``"tox -e py27"`` successfully locally, add
|
||||
``jinja2`` and ``markupsafe`` to your local ``test-requirements.txt``
|
||||
file so the two get installed in your local virtual environment.
|
25
HACKING.rst
25
HACKING.rst
@ -1,25 +0,0 @@
|
||||
openstack-doc-tools style commandments
|
||||
======================================
|
||||
|
||||
- Step 1: Read the OpenStack Style Commandments
|
||||
http://docs.openstack.org/developer/hacking/
|
||||
|
||||
- Step 2: Read on
|
||||
|
||||
Running tests
|
||||
-------------
|
||||
|
||||
So far there are some tests included with the package.
|
||||
|
||||
The openstack-indexpage tool is used while building the OpenStack
|
||||
documentation repositories, test building of these repositories with
|
||||
any changes done here.
|
||||
|
||||
Testing can be done with simply a local install of
|
||||
openstack-doc-tools, then checking out the repositories and
|
||||
running: ``tox`` inside of each.
|
||||
|
||||
The repositories using openstack-doc-tools include:
|
||||
* api-site
|
||||
* openstack-manuals
|
||||
* security-doc
|
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.
|
||||
|
12
MANIFEST.in
12
MANIFEST.in
@ -1,12 +0,0 @@
|
||||
include README.rst
|
||||
include AUTHORS
|
||||
include LICENSE
|
||||
include RELEASE_NOTES.rst
|
||||
recursive-include bin *
|
||||
recursive-include doc *
|
||||
recursive-include tools *
|
||||
recursive-include sitemap *
|
||||
recursive-include os_doc_tools *
|
||||
|
||||
recursive-exclude * .gitignore
|
||||
exclude .gitreview
|
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.
|
67
README.rst
67
README.rst
@ -1,67 +0,0 @@
|
||||
========================
|
||||
Team and repository tags
|
||||
========================
|
||||
|
||||
.. image:: http://governance.openstack.org/badges/openstack-doc-tools.svg
|
||||
:target: http://governance.openstack.org/reference/tags/index.html
|
||||
|
||||
.. Change things from this point on
|
||||
|
||||
OpenStack Doc Tools
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This repository contains tools used by the OpenStack Documentation
|
||||
project.
|
||||
|
||||
For more details, see the `OpenStack Documentation Contributor Guide
|
||||
<http://docs.openstack.org/contributor-guide/>`_.
|
||||
|
||||
* License: Apache License, Version 2.0
|
||||
* Source: https://git.openstack.org/cgit/openstack/openstack-doc-tools
|
||||
* Bugs: https://bugs.launchpad.net/openstack-doc-tools
|
||||
|
||||
Prerequisites
|
||||
-------------
|
||||
|
||||
You need to have Python 2.7 installed for using the tools.
|
||||
|
||||
This package needs a few external dependencies including lxml. If you
|
||||
do not have lxml installed, you can either install python-lxml or have
|
||||
it installed automatically and build from sources. To build lxml from
|
||||
sources, you need a C compiler and the xml and xslt development
|
||||
packages installed.
|
||||
|
||||
To install python-lxml, execute the following based on your
|
||||
distribution.
|
||||
|
||||
On Fedora, RHEL 7, and CentOS 7::
|
||||
|
||||
$ yum install python-lxml
|
||||
|
||||
On openSUSE::
|
||||
|
||||
$ zypper in python-lxml
|
||||
|
||||
On Ubuntu::
|
||||
|
||||
$ apt-get install python-lxml
|
||||
|
||||
For building from source, install the dependencies of lxml.
|
||||
|
||||
On Fedora, RHEL 7, and CentOS 7::
|
||||
|
||||
$ yum install python-devel libxml2-devel libxslt-devel
|
||||
|
||||
On openSUSE::
|
||||
|
||||
$ zypper in libxslt-devel
|
||||
|
||||
On Ubuntu::
|
||||
|
||||
$ apt-get install libxml2-dev libxslt-dev
|
||||
|
||||
|
||||
Regenerating config option tables
|
||||
---------------------------------
|
||||
|
||||
See :ref:`autogenerate_config_docs`.
|
@ -1,374 +0,0 @@
|
||||
=============
|
||||
Release notes
|
||||
=============
|
||||
|
||||
Current release notes
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Note that this file is now obsolete, we use reno for release note
|
||||
management and publish them at
|
||||
https://docs.openstack.org/releasenotes/openstack-doc-tools/ .
|
||||
|
||||
Add notes to releasenotes/notes directory following the
|
||||
documentation in
|
||||
https://docs.openstack.org/developer/reno/usage.html#creating-new-release-notes.
|
||||
|
||||
Older release notes
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
0.32.0
|
||||
------
|
||||
|
||||
* Removed a virtual build and test environment based on Vagrant.
|
||||
* Update ``autohelp-wrapper`` to support RST output.
|
||||
|
||||
0.31.0
|
||||
------
|
||||
|
||||
* Add ``doc-tools-build-rst`` to build RST guides.
|
||||
* Update sitemap to silence warnings.
|
||||
* Enhance ``openstack-auto-commands`` for new commands and releases.
|
||||
* Update ``autohelp.py`` for Liberty release.
|
||||
|
||||
0.30.1
|
||||
------
|
||||
|
||||
* ``openstack-auto-commands``: Fix option parsing (bug#1488505)
|
||||
* ``doc-tools-check-languages``: Fix RST Debian Install Guide.
|
||||
|
||||
0.30.0
|
||||
------
|
||||
|
||||
* ``openstack-doc-test``: Always built index page in checkbuild.
|
||||
* ``openstack-auto-commands``: Add support for murano.
|
||||
* Remove ``dn2osdbk`` and the ``hotref`` sphinx extension.
|
||||
* ``autohelp.py``: Can now find options for a project in multiple python
|
||||
packages.
|
||||
* ``doc-tools-check-languages``: Handle RST Install Guide and FirstApp.
|
||||
|
||||
0.29.1
|
||||
------
|
||||
|
||||
* ``doc-tools-check-languages``: Fix building of translated RST guides.
|
||||
|
||||
0.29.0
|
||||
------
|
||||
|
||||
* ``doc-tools-check-languages``: Handle common-rst directory, update
|
||||
for User Guides and firstapp.
|
||||
* ``autohelp.py``: Suport generation of RST tables, fixes for
|
||||
extensions.
|
||||
|
||||
0.28
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Sort entries in index.html file.
|
||||
* ``diff_branches.py``: Add options containing DEPRECATED in their help
|
||||
string to the deprecation list.
|
||||
* ``doc-tools-check-languages``: Fix bugs in RST handling that broke
|
||||
handling of user-guide and user-guide-admin.
|
||||
|
||||
0.27
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Do not build Debian Install Guide by
|
||||
default, built it only if the parameter ``--enable-debian-install``
|
||||
is passed. Fix index.html file and remove
|
||||
www files that came in by accident.
|
||||
|
||||
0.26
|
||||
----
|
||||
|
||||
* Fix ``doc-tools-check-languages`` handling of RST guides and
|
||||
publishing to translated draft guides.
|
||||
* Improve ``openstack-auto-commands``: bash-completion support for
|
||||
python-glanceclient, new commands for python-swiftclient, new command
|
||||
for trove-manage, automatically identify deprecated subcommands,
|
||||
move client definitions into a YAML resource file, support of the
|
||||
complete subcommand, support for new clients (barbican, designate, manila,
|
||||
magnetodb, manila, mistral, tuskar).
|
||||
|
||||
0.25
|
||||
----
|
||||
|
||||
* Enhance ``doc-tools-check-languages`` to handle translation of RST
|
||||
guides and publishing of draft guides to /draft/.
|
||||
* ``autohelp.py``: lookup configuration options in more oslo libraries.
|
||||
* ``autohelp.py``: add a hook for neutron plugins
|
||||
* ``autohelp-wrapper``: improve reliability by building a virtual env per
|
||||
project, rather than a common virtual env.
|
||||
* ``autohelp-wrapper``: define the custom dependencies for each project in
|
||||
their own requirements files.
|
||||
|
||||
0.24
|
||||
----
|
||||
|
||||
* Added ``doc-tools-update-cli-reference``, a wrapper script to update
|
||||
CLI references in the ``openstack-manuals`` repository.
|
||||
* Handle guides that published without a content/ sub directory.
|
||||
* Various fixes for auto generating commands and options.
|
||||
* Handle translation of RST guides.
|
||||
|
||||
0.23
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Don't build all books if only RST files are
|
||||
changed.
|
||||
|
||||
0.22
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: New niceness check to avoid specific unicode
|
||||
characters; new option --ignore-book to not build a book.
|
||||
|
||||
0.21.1
|
||||
------
|
||||
|
||||
* ``jsoncheck``: have formatted JSON files end with a newline (lp:bug 1403159)
|
||||
|
||||
0.21
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: New option ``--url-exception`` to ignore
|
||||
URLs in link check. Use jsoncheck in tests for more better tests and
|
||||
output.
|
||||
* ``openstack-auto-commands``: Update list of supported commands to
|
||||
include ironic, sahara
|
||||
* ``openstack-dn2osdbk``: Various fixes.
|
||||
|
||||
0.20
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Check for a ``\n`` in the last line of a file.
|
||||
* ``openstack-dn2osdbk``: Properly handle internal references.
|
||||
|
||||
0.19
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Optimize translation imports, improve output
|
||||
messages.
|
||||
* ``autohelp.py``: Improve sanitizer, better support for i18n in
|
||||
projects, allow setting of title name for tables.
|
||||
* ``autohelp-wrapper``: Smarter handling of the manuals repo and environment
|
||||
setup, add support for the ``create`` subcommand.
|
||||
* ``autohelp-wrapper``: Add support for offline/fast operation.
|
||||
* ``autohelp-wrapper``: Add a module blacklisting mechanism.
|
||||
* ``diff_branches.py``: Updated output format.
|
||||
* Provide a ``hotref`` extension for sphinx, to automate the creation of
|
||||
references to the HOT resources documentation.
|
||||
* ``openstack-auto-commands``: Handle python-openstackclient, handle
|
||||
python-glanceclient and python-cinderclient v2 commands.
|
||||
|
||||
0.18.1
|
||||
------
|
||||
|
||||
* Fix ``doc-tools-check-languages`` to handle all repositories and
|
||||
setups.
|
||||
|
||||
0.18
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Don't always build the HOT guide, add new
|
||||
option --check-links to check for valid URLs.
|
||||
* ``openstack-dn2osdbk``: Allow single files as source.
|
||||
* Imported and improved ``doc-tools-check-languages`` (recently known
|
||||
as ``tools/test-languages.sh`` in the documentation repositories).
|
||||
* Added a virtual build and test environment based on Vagrant.
|
||||
|
||||
0.17
|
||||
----
|
||||
|
||||
* Added support for ``*-manage`` CLI doc generation.
|
||||
* ``openstack-dn2osdbk``: Converts Docutils Native XML to docbook.
|
||||
* ``openstack-doc-test``: Handle the upcoming HOT guide.
|
||||
* ``autohelp.py``: Provide our own sanitizer.
|
||||
* ``autohelp.py``: Use the oslo sample_default if available.
|
||||
* ``openstack-doc-test``: Correctly handle SIGINT.
|
||||
* Various smaller fixes and improvements.
|
||||
|
||||
0.16.1
|
||||
------
|
||||
|
||||
* Fix includes of rackbook.rng to unbreak syntax checking.
|
||||
|
||||
0.16
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Fix handling of ignore-dir parameter.
|
||||
* ``autohelp-wrapper``: New tool to simplify the setup of an autohelp.py
|
||||
environment.
|
||||
* ``diff_branches.py``: Generates a listing of the configuration options
|
||||
changes that occurred between 2 openstack releases.
|
||||
* ``autohelp.py``: Add the 'dump' subcommand, include swift.
|
||||
* ``jsoncheck.py``: Add public API.
|
||||
* Added tool to generate a sitemap.xml file.
|
||||
* Added script to prettify HTML and XML syntax.
|
||||
|
||||
0.15
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Output information about tested patch,
|
||||
special case entity files for book building. Remove special handling
|
||||
for high-availability-guide, it is not using asciidoc anymore.
|
||||
* New script in cleanup/retf for spell checking using the RETF rules.
|
||||
patch.
|
||||
* Fix entity handling in ``openstack-generate-docbook``.
|
||||
|
||||
0.14
|
||||
----
|
||||
|
||||
* ``openstack-auto-commands``: Improved screen generation and swift
|
||||
subcommand xml output.
|
||||
* ``openstack-doc-test``: Warn about non-breaking space, enhance
|
||||
-v output, special case building of localized high-availability
|
||||
guide, fix for building changed identity-api repository.
|
||||
* New command ``openstack-jsoncheck`` to check for niceness of JSON
|
||||
files and reformat them.
|
||||
* ``openstack-autohelp``: Update the default parameters. The tables
|
||||
are generated in the doc/common/tables/ dir by default, and the git
|
||||
repository for the project being worked on is looked at in a sources/
|
||||
dir by default.
|
||||
|
||||
|
||||
0.13
|
||||
----
|
||||
|
||||
* ``extract_swift_flags``: Correctly parses existing tables and
|
||||
improve the output to ease the tables edition.
|
||||
* ``openstack-generate-docbook`` handles now the api-site project:
|
||||
Parameter --root gives root directory to use.
|
||||
* Remove obsoleted commands ``generatedocbook`` and
|
||||
``generatepot``. They have been obsoleted in 0.7.
|
||||
|
||||
0.12
|
||||
----
|
||||
|
||||
* ``openstack-doc-test``: Handle changes in api-site project, new
|
||||
option --print-unused-files.
|
||||
* ``openstack-autohelp``: Handle keystone_authtoken options.
|
||||
|
||||
0.11
|
||||
----
|
||||
|
||||
* Add ``--publish`` option to ``openstack-doc-test`` that does not
|
||||
publish the www directory to the wrong location.
|
||||
* Improvements for generation of option tables.
|
||||
|
||||
0.10
|
||||
----
|
||||
|
||||
* Fix ``openstack-doc-test`` to handle changes in ``api-site`` repository:
|
||||
Do not publish wadls directory, ``*.fo`` files and add api-ref-guides
|
||||
PDF files to index file for docs-draft.
|
||||
* Many improvements for generation of option tables.
|
||||
* Improvements for ``openstack-auto-commands``: handle ironic, sahara;
|
||||
improve generated output.
|
||||
|
||||
0.9
|
||||
---
|
||||
|
||||
Fixes for openstack-doc-test:
|
||||
|
||||
* openstack-doc-test now validates JSON files for well-formed-ness and
|
||||
whitespace.
|
||||
* Create proper chapter title for markdown files.
|
||||
* Ignore publish-docs directory completely.
|
||||
* Do not check for xml:ids in wadl resource.
|
||||
* New option build_file_exception to ignore invalid XML files for
|
||||
dependency checking in build and syntax checks.
|
||||
|
||||
Fixes for autodoc-tools to sanitize values and handle projects.
|
||||
|
||||
Client version number is output by openstack-auto-commands.
|
||||
|
||||
0.8.2
|
||||
-----
|
||||
|
||||
Fixes for openstack-doc-test:
|
||||
|
||||
* Fix error handling, now really abort if an error occurs.
|
||||
* Avoid races in initial maven setup that broke build.
|
||||
* Add --parallel/noparallel flags to disable parallel building.
|
||||
|
||||
0.8.1
|
||||
-----
|
||||
|
||||
* Fix openstack-doc-test building of image-api.
|
||||
* Fix publishing of api-ref.
|
||||
* Improve markdown conversion.
|
||||
|
||||
0.8
|
||||
---
|
||||
|
||||
* Improved openstack-auto-commands output
|
||||
* Fix script invocation in openstack-doc-test.
|
||||
|
||||
0.7.1
|
||||
-----
|
||||
|
||||
* Fix openstack-doc-test niceness and syntax checks that always
|
||||
failed in api projects.
|
||||
* Fix building of image-api-v2
|
||||
|
||||
0.7
|
||||
---
|
||||
|
||||
* openstack-doc-test:
|
||||
|
||||
- Fix building of identity-api and image-api books.
|
||||
- Add option --debug.
|
||||
- Generate log file for each build.
|
||||
- Do not install build-ha-guide.sh and markdown-docbook.sh in
|
||||
/usr/bin, use special scripts dir instead.
|
||||
- Allow to configure the directory used under publish-doc
|
||||
|
||||
* generatedocbook and generatepot have been merged into a single
|
||||
file, the command has been renamed to
|
||||
openstack-generate-docbook/openstack-generate-pot. For
|
||||
compatibility, wrapper scripts are installed that will be removed
|
||||
in version 0.8.
|
||||
|
||||
0.6
|
||||
---
|
||||
|
||||
* Fix python packaging bugs that prevented sitepackages usage and
|
||||
installed .gitignore in packages
|
||||
|
||||
0.5
|
||||
---
|
||||
|
||||
* Test that resources in wadl files have an xml:id (lp:bug 1275007).
|
||||
* Improve formatting of python command line clients (lp:bug 1274699).
|
||||
* Copy all generated books to directory publish-docs in the git
|
||||
top-level (lp:blueprint draft-docs-on-docs-draft).
|
||||
* Requires now a config file in top-level git directory named
|
||||
doc-test.conf.
|
||||
* Allow building of translated manuals, these need to be setup first
|
||||
with "generatedocbook -l LANGUAGE -b BOOK".
|
||||
|
||||
0.4
|
||||
---
|
||||
|
||||
* New option --exceptions-file to pass list of files to ignore
|
||||
completely.
|
||||
* Major improvements for automatic generation of option tables.
|
||||
* New tool openstack-auto-commands to document python
|
||||
command line clients.
|
||||
|
||||
0.3
|
||||
---
|
||||
|
||||
* Fixes path for automated translation toolchain to fix lp:bug 1216153.
|
||||
* Validates .xsd .xsl and.xjb files in addition to .xml.
|
||||
* Fixes validation of WADL files to validate properly against XML schema.
|
||||
|
||||
0.2
|
||||
---
|
||||
|
||||
* Enables local copies of RNG schema for validation.
|
||||
* Enables ignoring directories when checking.
|
||||
|
||||
0.1
|
||||
---
|
||||
|
||||
Initial release.
|
@ -1,121 +0,0 @@
|
||||
.. _autogenerate_config_docs:
|
||||
|
||||
autogenerate_config_docs
|
||||
========================
|
||||
|
||||
Automatically generate configuration tables to document OpenStack.
|
||||
|
||||
Using the wrapper
|
||||
-----------------
|
||||
|
||||
``autohelp-wrapper`` is the recommended tool to generate the configuration
|
||||
tables. Don't bother using ``autohelp.py`` manually.
|
||||
|
||||
The ``autohelp-wrapper`` script installs a virtual environment and all the
|
||||
needed dependencies, clones or updates the projects and manuals repositories,
|
||||
then runs the ``autohelp.py`` script in the virtual environment.
|
||||
|
||||
New and updated flagmappings are generated in the ``openstack-manuals``
|
||||
repository (``tools/autogenerate-config-flagmappings/`` directory).
|
||||
|
||||
Prior to running the following commands, you need to install several development
|
||||
packages.
|
||||
|
||||
On Ubuntu:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt-get install python-dev python-pip python-virtualenv \
|
||||
libxml2-dev libxslt1-dev zlib1g-dev \
|
||||
libmysqlclient-dev libpq-dev libffi-dev \
|
||||
libsqlite3-dev libldap2-dev libsasl2-dev \
|
||||
libjpeg-dev
|
||||
|
||||
On RHEL 7 and CentOS 7:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo yum install https://www.rdoproject.org/repos/rdo-release.rpm
|
||||
$ sudo yum update
|
||||
$ sudo yum install python-devel python-pip python-virtualenv \
|
||||
libxml2-devel libxslt-devel zlib-devel \
|
||||
mariadb-devel postgresql-devel libffi-devel \
|
||||
sqlite-devel openldap-devel cyrus-sasl-devel \
|
||||
libjpeg-turbo-devel gcc git
|
||||
|
||||
.. note::
|
||||
* libjpeg is needed for ironic
|
||||
|
||||
The workflow is:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install -rrequirements.txt
|
||||
$ ./autohelp-wrapper update
|
||||
$ $EDITOR sources/openstack-manuals/tools/autogenerate-config-flagmappings/*.flagmappings
|
||||
$ ./autohelp-wrapper rst
|
||||
$ # check the results in sources/openstack-manuals
|
||||
|
||||
This will generate the tables for all the known projects.
|
||||
Note for neutron project: If the driver/plugin resides outside the neutron
|
||||
repository, then the driver/plugin has to be explicitly installed within the
|
||||
virtual environment to generate the configuration options.
|
||||
|
||||
To generate the mappings and tables for a subset of projects, use the code
|
||||
names as arguments:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./autohelp-wrapper update cinder heat
|
||||
$ # edit the mappings files
|
||||
$ ./autohelp-wrapper rst cinder heat
|
||||
|
||||
|
||||
Flagmappings files
|
||||
------------------
|
||||
|
||||
The tool uses flagmapping files to map options to custom categories. Flag
|
||||
mapping files can be found in the ``tools/autogenerate-config-flagmappings``
|
||||
folder of the openstack-manuals project. Not all projects use flagmapping
|
||||
files, as those that do not will be disabled by the presence of a
|
||||
``$project.disable`` file in that folder. For those that do, however, the files
|
||||
use the following format::
|
||||
|
||||
OPTION_SECTION/OPTION_NAME group1 [group2, ...]
|
||||
|
||||
Groups need to be defined manually to organize the configuration tables.
|
||||
|
||||
The group values can only contain alphanumeric characters, _ and - (they will
|
||||
be used as document IDs).
|
||||
|
||||
To make the table titles more user friendly, create or edit the PROJECT.headers
|
||||
file in the manuals repository. Each line of this file is of the form:
|
||||
|
||||
::
|
||||
|
||||
GROUP A Nice Title
|
||||
|
||||
Working with branches
|
||||
---------------------
|
||||
|
||||
``autohelp-wrapper`` works on the master branch by default, but you can tell it
|
||||
to work on another branch:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./autohelp-wrapper -b stable/liberty update
|
||||
|
||||
.. note::
|
||||
The ``-b`` switch doesn't apply to the ``openstack-manuals`` repository
|
||||
which will be left untouched (no ``git branch``, no ``git update``).
|
||||
|
||||
|
||||
Generate configuration difference
|
||||
---------------------------------
|
||||
|
||||
To generate "New, updated, and deprecated options" for each service,
|
||||
run ``diff_branches.py``. For example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ ./diff_branches.py stable/liberty stable/mitaka nova
|
@ -1,18 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# 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 pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo('openstack-doc-tools').version_string()
|
@ -1,273 +0,0 @@
|
||||
#!/bin/bash
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
|
||||
HERE=$(pwd)
|
||||
VENVDIR=$HERE/venv
|
||||
SOURCESDIR=$HERE/sources
|
||||
MANUALSREPO=$SOURCESDIR/openstack-manuals
|
||||
MAPPINGS_DIR=$MANUALSREPO/tools/autogenerate-config-flagmappings
|
||||
AUTOHELP="python $HERE/autohelp.py"
|
||||
GITBASE=${GITBASE:-git://git.openstack.org/openstack}
|
||||
GITPROJ=${GITPROJ:-git://git.openstack.org/openstack}
|
||||
PROJECTS="aodh ceilometer cinder glance heat ironic keystone manila \
|
||||
murano neutron nova sahara senlin trove zaqar"
|
||||
MANUALS_PROJECTS="openstack-manuals"
|
||||
BRANCH=master
|
||||
FAST=0
|
||||
QUIET=0
|
||||
CLONE_MANUALS=1
|
||||
|
||||
usage() {
|
||||
echo "Wrapper for autohelp.py"
|
||||
echo "Usage:"
|
||||
echo " $(basename $0) [ OPTIONS ] dump|update|rst|setup [ project... ]"
|
||||
echo
|
||||
echo "Subcommands:"
|
||||
echo " dump: Dumps the list of options with their attributes"
|
||||
echo " update: Update or create the flagmapping files"
|
||||
echo " rst: Generate the options tables in RST format"
|
||||
echo " setup: Install the environment only"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -b BRANCH: Work on this branch (defaults to master)"
|
||||
echo " -g GITPROJ: Use this location for the project git repos "
|
||||
echo " (defaults to git://git.openstack.org/openstack)"
|
||||
echo " -c: Recreate the virtual environment"
|
||||
echo " -f: Work offline: Do not change environment or sources"
|
||||
echo " -e PATH: Create the virtualenv in PATH"
|
||||
echo " -v LEVEL: Verbose message (1 or 2)"
|
||||
echo " (check various python modules imported or not)"
|
||||
echo " -o OUTDIR: Path to output openstack-manuals directory "
|
||||
echo " (defaults to ./sources/openstack-manuals)"
|
||||
}
|
||||
|
||||
setup_venv() {
|
||||
project=$1
|
||||
|
||||
if [ ! -e $VENVDIR/$project/bin/activate ]; then
|
||||
mkdir -p $VENVDIR/$project
|
||||
virtualenv $VENVDIR/$project
|
||||
fi
|
||||
activate_venv $project
|
||||
}
|
||||
|
||||
activate_venv() {
|
||||
project=$1
|
||||
|
||||
. $VENVDIR/$project/bin/activate
|
||||
pip install --upgrade pip setuptools
|
||||
}
|
||||
|
||||
get_project() {
|
||||
project=$1
|
||||
git_url=$GITPROJ
|
||||
|
||||
if [ ! -e $SOURCESDIR/$project ]; then
|
||||
if [[ $MANUALS_PROJECTS =~ (^| )$project($| ) ]]; then
|
||||
git_url=$GITBASE
|
||||
fi
|
||||
git clone $git_url/$project $SOURCESDIR/$project
|
||||
|
||||
if [ -e $MAPPINGS_DIR/$project.extra_repos ]; then
|
||||
while read extra; do
|
||||
git clone $git_url/$extra $SOURCESDIR/$extra
|
||||
done < $MAPPINGS_DIR/$project.extra_repos
|
||||
fi
|
||||
|
||||
else
|
||||
if [ $project != openstack-manuals ]; then
|
||||
(cd $SOURCESDIR/$project && git pull)
|
||||
fi
|
||||
|
||||
if [ -e $MAPPINGS_DIR/$project.extra_repos ]; then
|
||||
while read extra; do
|
||||
(cd $SOURCESDIR/$extra && git pull)
|
||||
done < $MAPPINGS_DIR/$project.extra_repos
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
setup_tools() {
|
||||
pip install -rrequirements.txt
|
||||
}
|
||||
|
||||
while getopts :b:g:e:o:v:cfq opt; do
|
||||
case $opt in
|
||||
b)
|
||||
BRANCH=$OPTARG
|
||||
;;
|
||||
g)
|
||||
GITPROJ=$OPTARG
|
||||
;;
|
||||
c)
|
||||
rm -rf $VENVDIR
|
||||
;;
|
||||
e)
|
||||
VENVDIR=$OPTARG
|
||||
;;
|
||||
o)
|
||||
MANUALSREPO=$OPTARG
|
||||
MAPPINGS_DIR=$OPTARG/tools/autogenerate-config-flagmappings
|
||||
CLONE_MANUALS=0
|
||||
;;
|
||||
f)
|
||||
FAST=1
|
||||
;;
|
||||
q)
|
||||
QUIET=1
|
||||
;;
|
||||
v)
|
||||
AUTOOPT="-v"
|
||||
if [ $OPTARG = 2 ]; then
|
||||
AUTOOPT="-vv"
|
||||
fi
|
||||
;;
|
||||
\?)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ACTION=$1
|
||||
shift
|
||||
|
||||
if [ $QUIET -eq 1 ]; then
|
||||
exec 3>&1 >/dev/null
|
||||
exec 4>&2 2>/dev/null
|
||||
fi
|
||||
|
||||
case $ACTION in
|
||||
update|rst|dump|setup) ;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -e autohelp.py ]; then
|
||||
echo "Execute this script in the autogenerate_config_docs directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[ $# != 0 ] && PROJECTS="$*"
|
||||
|
||||
RELEASE=$(echo $BRANCH | sed 's,^stable.,,')
|
||||
|
||||
if [ "$FAST" -eq 0 ] ; then
|
||||
if [ "$CLONE_MANUALS" -eq 1 ] ; then
|
||||
get_project openstack-manuals
|
||||
fi
|
||||
|
||||
for project in $PROJECTS; do
|
||||
setup_venv $project
|
||||
setup_tools
|
||||
if [ -e $MAPPINGS_DIR/$project.requirements ]; then
|
||||
pip install -r $MAPPINGS_DIR/$project.requirements \
|
||||
--allow-all-external
|
||||
fi
|
||||
get_project $project
|
||||
|
||||
(
|
||||
pushd $SOURCESDIR/$project
|
||||
module=$(echo $project | tr - _ )
|
||||
find $module -name "*.pyc" -delete
|
||||
GIT_CMD="git show-ref --verify --quiet refs/heads/$BRANCH"
|
||||
if $GIT_CMD; then
|
||||
git checkout $BRANCH
|
||||
else
|
||||
git checkout -b $BRANCH remotes/origin/$BRANCH
|
||||
fi
|
||||
pip install -rrequirements.txt
|
||||
[ -e "test-requirements.txt" ] && \
|
||||
pip install -rtest-requirements.txt
|
||||
python setup.py install
|
||||
popd
|
||||
|
||||
if [ -e $MAPPINGS_DIR/$project.extra_repos ]; then
|
||||
while read extra; do
|
||||
(
|
||||
cd $SOURCESDIR/$extra
|
||||
pip install -rrequirements.txt
|
||||
[ -e "test-requirements.txt" ] && \
|
||||
pip install -rtest-requirements.txt
|
||||
python setup.py install
|
||||
)
|
||||
done < $MAPPINGS_DIR/$project.extra_repos
|
||||
fi
|
||||
)
|
||||
done
|
||||
fi
|
||||
|
||||
for project in $PROJECTS; do
|
||||
echo "Working on $project..."
|
||||
activate_venv $project
|
||||
if [ "$ACTION" = "setup" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
if [ -e $MAPPINGS_DIR/$project.extra_repos ]; then
|
||||
extra_flags=
|
||||
while read extra; do
|
||||
package=$(echo $extra | tr - _)
|
||||
if [ $package = "networking_midonet" ]; then
|
||||
package="midonet"
|
||||
fi
|
||||
if [ $package = "networking_hyperv" ]; then
|
||||
package="hyperv"
|
||||
fi
|
||||
if [ $package = "networking_edge_vpn" ]; then
|
||||
package="networking-edge-vpn"
|
||||
fi
|
||||
if [ $package = "networking_zvm" ]; then
|
||||
package="neutron"
|
||||
cp -r $SOURCESDIR/networking-zvm/neutron/plugins/zvm \
|
||||
$SOURCESDIR/neutron/neutron/plugins
|
||||
cp -r \
|
||||
$SOURCESDIR/networking-zvm/neutron/plugins/ml2/drivers/zvm\
|
||||
$SOURCESDIR/neutron/neutron/plugins/ml2/drivers
|
||||
fi
|
||||
extra_flags="$extra_flags -i $SOURCESDIR/$extra/$package"
|
||||
done < $MAPPINGS_DIR/$project.extra_repos
|
||||
fi
|
||||
|
||||
cd $MAPPINGS_DIR
|
||||
|
||||
case $ACTION in
|
||||
update)
|
||||
$AUTOHELP update $project -i $SOURCESDIR/$project/$project \
|
||||
$extra_flags $AUTOOPT
|
||||
mv $project.flagmappings.new $project.flagmappings
|
||||
;;
|
||||
rst)
|
||||
$AUTOHELP rst $project -i $SOURCESDIR/$project/$project \
|
||||
$extra_flags $AUTOOPT
|
||||
;;
|
||||
dump)
|
||||
if [ $QUIET -eq 1 ]; then
|
||||
exec 1>&3
|
||||
exec 2>&4
|
||||
fi
|
||||
$AUTOHELP dump $project -i $SOURCESDIR/$project/$project \
|
||||
$extra_flags $AUTOOPT
|
||||
;;
|
||||
esac
|
||||
done
|
@ -1,696 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
#
|
||||
# A collection of tools for working with flags from OpenStack
|
||||
# packages and documentation.
|
||||
#
|
||||
# For an example of usage, run this program with the -h switch.
|
||||
#
|
||||
|
||||
# Must import this before argparse
|
||||
from oslo_config import cfg
|
||||
|
||||
import argparse
|
||||
import importlib
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
import sys
|
||||
|
||||
import jinja2
|
||||
import stevedore
|
||||
|
||||
try:
|
||||
from sqlalchemy import exc
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sys.path.insert(0, '.')
|
||||
try:
|
||||
from hooks import HOOKS # noqa
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
EXTENSIONS = ['oslo.cache',
|
||||
'oslo.concurrency',
|
||||
'oslo.db',
|
||||
'oslo.log',
|
||||
'oslo.messaging',
|
||||
'oslo.middleware',
|
||||
'oslo.policy',
|
||||
'oslo.service']
|
||||
|
||||
_TYPE_DESCRIPTIONS = {
|
||||
cfg.StrOpt: 'String',
|
||||
cfg.BoolOpt: 'Boolean',
|
||||
cfg.IntOpt: 'Integer',
|
||||
cfg.FloatOpt: 'Floating point',
|
||||
cfg.ListOpt: 'List',
|
||||
cfg.DictOpt: 'Dict',
|
||||
cfg.MultiStrOpt: 'Multi-valued',
|
||||
cfg.IPOpt: 'IP address',
|
||||
cfg.PortOpt: 'Port number',
|
||||
cfg.HostnameOpt: 'Hostname',
|
||||
cfg.URIOpt: 'URI',
|
||||
cfg.HostAddressOpt: 'Host address',
|
||||
cfg._ConfigFileOpt: 'List of filenames',
|
||||
cfg._ConfigDirOpt: 'List of directory names',
|
||||
}
|
||||
|
||||
register_re = re.compile(r'''^ +.*\.register_opts\((?P<opts>[^,)]+)'''
|
||||
r'''(, (group=)?["'](?P<group>.*)["'])?\)''')
|
||||
|
||||
|
||||
def import_modules(repo_location, package_name, verbose=0):
|
||||
"""Import modules.
|
||||
|
||||
Loops through the repository, importing module by module to
|
||||
populate the configuration object (cfg.CONF) created from Oslo.
|
||||
"""
|
||||
|
||||
with open('ignore.list') as fd:
|
||||
ignore_list = [l for l in fd.read().split('\n')
|
||||
if l and (l[0] != '#')]
|
||||
|
||||
pkg_location = os.path.join(repo_location, package_name)
|
||||
for root, dirs, files in os.walk(pkg_location):
|
||||
skipdir = False
|
||||
for excludedir in ('tests', 'locale',
|
||||
os.path.join('db', 'migration'), 'transfer'):
|
||||
if ((os.path.sep + excludedir + os.path.sep) in root or (
|
||||
root.endswith(os.path.sep + excludedir))):
|
||||
skipdir = True
|
||||
break
|
||||
if skipdir:
|
||||
continue
|
||||
for pyfile in files:
|
||||
if pyfile.endswith('.py'):
|
||||
abs_path = os.path.join(root, pyfile)
|
||||
modfile = abs_path.split(repo_location, 1)[1]
|
||||
modname = os.path.splitext(modfile)[0].split(os.path.sep)
|
||||
modname = [m for m in modname if m != '']
|
||||
modname = '.'.join(modname)
|
||||
if modname.endswith('.__init__'):
|
||||
modname = modname[:modname.rfind(".")]
|
||||
if modname in ignore_list:
|
||||
continue
|
||||
try:
|
||||
module = importlib.import_module(modname)
|
||||
if verbose >= 1:
|
||||
print("imported %s" % modname)
|
||||
except ImportError as e:
|
||||
"""
|
||||
work around modules that don't like being imported in
|
||||
this way FIXME This could probably be better, but does
|
||||
not affect the configuration options found at this stage
|
||||
"""
|
||||
if verbose >= 2:
|
||||
print("Failed to import: %s (%s)" % (modname, e))
|
||||
continue
|
||||
except cfg.DuplicateOptError as e:
|
||||
"""
|
||||
oslo.cfg doesn't allow redefinition of a config option, but
|
||||
we don't mind. Don't fail if this happens.
|
||||
"""
|
||||
if verbose >= 2:
|
||||
print(e)
|
||||
continue
|
||||
except cfg.NoSuchGroupError as e:
|
||||
"""
|
||||
If a group doesn't exist, we ignore the import.
|
||||
"""
|
||||
if verbose >= 2:
|
||||
print(e)
|
||||
continue
|
||||
except exc.InvalidRequestError as e:
|
||||
if verbose >= 2:
|
||||
print(e)
|
||||
continue
|
||||
except Exception as e:
|
||||
print("Impossible to import module %s" % modname)
|
||||
raise
|
||||
|
||||
_register_runtime_opts(module, abs_path, verbose)
|
||||
_run_hook(modname)
|
||||
|
||||
# All the components provide keystone token authentication, usually using a
|
||||
# pipeline. Since the auth_token options can only be discovered at runtime
|
||||
# in this configuration, we force their discovery by importing the module.
|
||||
try:
|
||||
import keystonemiddleware.auth_token # noqa
|
||||
except cfg.DuplicateOptError:
|
||||
pass
|
||||
|
||||
|
||||
def _run_hook(modname):
|
||||
try:
|
||||
HOOKS[modname]()
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def _register_runtime_opts(module, abs_path, verbose):
|
||||
"""Handle options not registered on module import.
|
||||
|
||||
This function parses the .py files to discover calls to register_opts in
|
||||
functions and methods. It then explicitly call cfg.register_opt on each
|
||||
option to register (most of) them.
|
||||
"""
|
||||
|
||||
with open(abs_path) as fd:
|
||||
for line in fd:
|
||||
m = register_re.search(line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
opts_var = m.group('opts')
|
||||
opts_group = m.group('group')
|
||||
|
||||
# Get the object (an options list) from the opts_var string.
|
||||
# This requires parsing the string which can be of the form
|
||||
# 'foo.bar'. We treat each element as an attribute of the previous.
|
||||
register = True
|
||||
obj = module
|
||||
for item in opts_var.split('.'):
|
||||
try:
|
||||
obj = getattr(obj, item)
|
||||
except AttributeError:
|
||||
# FIXME(gpocentek): AttributeError is raised when a part of
|
||||
# the opts_var string is not an actual attribute. This will
|
||||
# need more parsing tricks.
|
||||
register = False
|
||||
if verbose >= 2:
|
||||
print("Ignoring %(obj)s in %(module)s" %
|
||||
{'obj': opts_var, 'module': module})
|
||||
break
|
||||
|
||||
if register and isinstance(obj, list):
|
||||
for opt in obj:
|
||||
if not isinstance(opt, cfg.Opt):
|
||||
continue
|
||||
try:
|
||||
cfg.CONF.register_opt(opt, opts_group)
|
||||
except cfg.DuplicateOptError:
|
||||
# ignore options that have already been registered
|
||||
pass
|
||||
|
||||
|
||||
def _sanitize_default(opt):
|
||||
"""Adapts unrealistic default values."""
|
||||
|
||||
# If the Oslo version is recent enough, we can use the 'sample_default'
|
||||
# attribute
|
||||
if (hasattr(opt, 'sample_default') and opt.sample_default is not None):
|
||||
return str(opt.sample_default)
|
||||
|
||||
if ((type(opt).__name__ == "ListOpt") and (type(opt.default) == list)):
|
||||
return ", ".join(str(item) for item in opt.default)
|
||||
|
||||
default = str(opt.default)
|
||||
|
||||
if default == os.uname()[1]:
|
||||
return 'localhost'
|
||||
|
||||
if opt.name == 'bindir':
|
||||
return '/usr/local/bin'
|
||||
|
||||
if opt.name == 'my_ip':
|
||||
return '10.0.0.1'
|
||||
|
||||
if isinstance(opt, cfg.StrOpt) and default.strip() != default:
|
||||
return '"%s"' % default
|
||||
|
||||
for pathelm in sys.path:
|
||||
if pathelm in ('.', ''):
|
||||
continue
|
||||
if pathelm.endswith('/'):
|
||||
pathelm = pathelm[:-1]
|
||||
if pathelm in default:
|
||||
default = re.sub(r'%s(/sources)?' % pathelm,
|
||||
'/usr/lib/python/site-packages', default)
|
||||
|
||||
return default
|
||||
|
||||
|
||||
def _get_overrides(package_name):
|
||||
overrides_file = '%s.overrides' % package_name
|
||||
if not os.path.exists(overrides_file):
|
||||
return {}
|
||||
overrides = {}
|
||||
with open(overrides_file) as fd:
|
||||
for line in fd:
|
||||
if line == '#':
|
||||
continue
|
||||
try:
|
||||
opt, sections = line.strip().split(' ', 1)
|
||||
sections = [x.strip() for x in sections.split(' ')]
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
overrides[opt] = sections
|
||||
|
||||
return overrides
|
||||
|
||||
|
||||
class OptionsCache(object):
|
||||
def __init__(self, overrides={}, verbose=0):
|
||||
self._verbose = verbose
|
||||
self._opts_by_name = {}
|
||||
self._opts_by_group = {}
|
||||
self._opt_names = []
|
||||
self._overrides = overrides
|
||||
|
||||
for optname in cfg.CONF._opts:
|
||||
opt = cfg.CONF._opts[optname]['opt']
|
||||
# We ignore some CLI opts by excluding SubCommandOpt objects
|
||||
if not isinstance(opt, cfg.SubCommandOpt):
|
||||
self._add_opt(optname, 'DEFAULT', opt)
|
||||
|
||||
for group in cfg.CONF._groups:
|
||||
for optname in cfg.CONF._groups[group]._opts:
|
||||
self._add_opt(group + '/' + optname, group,
|
||||
cfg.CONF._groups[group]._opts[optname]['opt'])
|
||||
|
||||
self._opt_names.sort(OptionsCache._cmpopts)
|
||||
|
||||
def _add_opt(self, optname, group, opt):
|
||||
if optname in self._opts_by_name:
|
||||
if self._verbose >= 2:
|
||||
print("Duplicate option name %s" % optname)
|
||||
return
|
||||
|
||||
opt.default = _sanitize_default(opt)
|
||||
|
||||
def fill(optname, group, opt):
|
||||
if optname in self._opts_by_name:
|
||||
return
|
||||
self._opts_by_name[optname] = (group, opt)
|
||||
self._opt_names.append(optname)
|
||||
|
||||
if group not in self._opts_by_group:
|
||||
self._opts_by_group[group] = []
|
||||
|
||||
self._opts_by_group[group].append(opt)
|
||||
|
||||
if optname in self._overrides:
|
||||
for new_group in self._overrides[optname]:
|
||||
if new_group == 'DEFAULT':
|
||||
new_optname = opt.name
|
||||
else:
|
||||
new_optname = new_group + '/' + opt.name
|
||||
fill(new_optname, new_group, opt)
|
||||
|
||||
else:
|
||||
fill(optname, group, opt)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._opt_names)
|
||||
|
||||
def load_extension_options(self, module):
|
||||
# Note that options loaded this way aren't added to _opts_by_module
|
||||
loader = stevedore.named.NamedExtensionManager(
|
||||
'oslo.config.opts',
|
||||
names=(module,),
|
||||
invoke_on_load=False
|
||||
)
|
||||
for ext in loader:
|
||||
for group, opts in ext.plugin():
|
||||
for opt in opts:
|
||||
if group is None:
|
||||
self._add_opt(opt.dest, 'DEFAULT', opt)
|
||||
else:
|
||||
self._add_opt(group + '/' + opt.dest, group, opt)
|
||||
|
||||
self._opt_names.sort(OptionsCache._cmpopts)
|
||||
|
||||
def maybe_load_extensions(self, repositories):
|
||||
# Use the requirements.txt of the project to guess if an oslo module
|
||||
# needs to be imported
|
||||
needed_exts = set()
|
||||
for repo in repositories:
|
||||
base_path = os.path.dirname(repo)
|
||||
for ext in EXTENSIONS:
|
||||
requirements = os.path.join(base_path, 'requirements.txt')
|
||||
with open(requirements) as fd:
|
||||
for line in fd:
|
||||
if line.startswith(ext):
|
||||
needed_exts.add(ext)
|
||||
|
||||
for ext in needed_exts:
|
||||
self.load_extension_options(ext)
|
||||
|
||||
def get_group_names(self):
|
||||
return self._opts_by_group.keys()
|
||||
|
||||
def get_option_names(self):
|
||||
return self._opt_names
|
||||
|
||||
def get_group(self, name):
|
||||
return self._opts_by_group[name]
|
||||
|
||||
def get_option(self, name):
|
||||
return self._opts_by_name[name]
|
||||
|
||||
def dump(self):
|
||||
"""Dumps the list of options with their attributes.
|
||||
|
||||
This output is consumed by the diff_branches script.
|
||||
"""
|
||||
for name, (group, option) in self._opts_by_name.items():
|
||||
deprecated_opts = [{'group': deprecated.group,
|
||||
'name': deprecated.name}
|
||||
for deprecated in option.deprecated_opts]
|
||||
help_str = option.help.strip() if option.help else "None"
|
||||
new_option = {
|
||||
'default': option.default,
|
||||
'help': help_str,
|
||||
'deprecated_opts': deprecated_opts,
|
||||
'type': option.__class__.__name__.split('.')[-1]
|
||||
}
|
||||
self._opts_by_name[name] = (group, new_option)
|
||||
print(pickle.dumps(self._opts_by_name))
|
||||
|
||||
@staticmethod
|
||||
def _cmpopts(x, y):
|
||||
if '/' in x and '/' in y:
|
||||
prex = x[:x.find('/')]
|
||||
prey = y[:y.find('/')]
|
||||
if prex != prey:
|
||||
return cmp(prex, prey)
|
||||
return cmp(x, y)
|
||||
elif '/' in x:
|
||||
return 1
|
||||
elif '/' in y:
|
||||
return -1
|
||||
else:
|
||||
return cmp(x, y)
|
||||
|
||||
|
||||
def _use_categories(package_name):
|
||||
return not os.path.isfile(package_name + '.disable')
|
||||
|
||||
|
||||
def _get_options_by_cat(package_name):
|
||||
options_by_cat = {}
|
||||
|
||||
with open(package_name + '.flagmappings') as f:
|
||||
for line in f:
|
||||
if not line.strip() or line.startswith('#'):
|
||||
continue
|
||||
opt, categories = line.split(' ', 1)
|
||||
for category in categories.split():
|
||||
options_by_cat.setdefault(category, []).append(opt)
|
||||
|
||||
return options_by_cat
|
||||
|
||||
|
||||
def _get_category_names(package_name):
|
||||
package_headers = package_name + '.headers'
|
||||
category_names = {}
|
||||
for headers_file in ('shared.headers', package_headers):
|
||||
try:
|
||||
with open(headers_file) as f:
|
||||
for line in f:
|
||||
if not line.strip() or line.startswith('#'):
|
||||
continue
|
||||
cat, nice_name = line.split(' ', 1)
|
||||
category_names[cat] = nice_name.strip()
|
||||
except IOError:
|
||||
print("Cannot open %s (ignored)" % headers_file)
|
||||
|
||||
return category_names
|
||||
|
||||
|
||||
def _format_opt(option):
|
||||
|
||||
def _remove_prefix(text, prefix):
|
||||
if text.startswith(prefix):
|
||||
return text[len(prefix):].lstrip(':')
|
||||
return text
|
||||
|
||||
def _reflow_text(text):
|
||||
text = re.sub(r'\n+\s*\* ', '$sentinal$* ', text)
|
||||
text = text.replace('\n\n', '$sentinal$')
|
||||
text = text.replace('\n', ' ')
|
||||
text = ' '.join(text.split())
|
||||
return text.split('$sentinal$')
|
||||
|
||||
def _strip_indentation(text):
|
||||
return ' '.join([x.strip() for x in text.split('\n')]).strip()
|
||||
|
||||
help_text = option.help or "No help text available for this option."
|
||||
help_text = _remove_prefix(help_text.strip(), 'DEPRECATED')
|
||||
help_text = _reflow_text(help_text)
|
||||
|
||||
deprecated_text = (option.deprecated_reason or
|
||||
'No deprecation reason provided for this option.')
|
||||
deprecated_text = _strip_indentation(deprecated_text)
|
||||
|
||||
opt_type = _TYPE_DESCRIPTIONS.get(type(option), 'Unknown')
|
||||
|
||||
flags = []
|
||||
|
||||
if (option.deprecated_for_removal or
|
||||
(option.help and option.help.startswith('DEPRECATED'))):
|
||||
flags.append(('Deprecated', deprecated_text))
|
||||
if option.mutable:
|
||||
flags.append(('Mutable', 'This option can be changed without'
|
||||
' restarting.'))
|
||||
|
||||
return {
|
||||
'name': option.dest,
|
||||
'type': opt_type,
|
||||
'default': _sanitize_default(option),
|
||||
'help': help_text,
|
||||
'flags': flags
|
||||
}
|
||||
|
||||
|
||||
def write_files(package_name, options, target):
|
||||
"""Write tables.
|
||||
|
||||
Some projects make use of flagmapping files, while others make use
|
||||
of oslo.config's OptGroup to do the same in code. The function will
|
||||
handle both, printing a list of options by either category or group.
|
||||
"""
|
||||
target = target or '../../doc/config-reference/source/tables'
|
||||
if not os.path.isdir(target):
|
||||
os.makedirs(target)
|
||||
|
||||
if _use_categories(package_name):
|
||||
_write_files_by_category(package_name, options, target)
|
||||
else:
|
||||
_write_files_by_group(package_name, options, target)
|
||||
|
||||
|
||||
def _write_files_by_category(package_name, options, target):
|
||||
options_by_cat = _get_options_by_cat(package_name)
|
||||
category_names = _get_category_names(package_name)
|
||||
|
||||
for cat in options_by_cat.keys():
|
||||
env = {
|
||||
'pkg': package_name,
|
||||
'cat': cat,
|
||||
'label': '-'.join([package_name, cat]),
|
||||
'groups': [],
|
||||
'items': [],
|
||||
}
|
||||
|
||||
# Skip the options that is explicitly marked as disabled,
|
||||
# which is used for common configuration options.
|
||||
if cat == 'disable':
|
||||
continue
|
||||
|
||||
if cat in category_names:
|
||||
env['nice_cat'] = category_names[cat]
|
||||
else:
|
||||
env['nice_cat'] = cat
|
||||
print("No nicename for %s" % cat)
|
||||
|
||||
curgroup = None
|
||||
items = None
|
||||
for optname in options_by_cat[cat]:
|
||||
group, option = options.get_option(optname)
|
||||
|
||||
if group != curgroup:
|
||||
if group is not None:
|
||||
curgroup = group
|
||||
env['groups'].append(group)
|
||||
if items is not None:
|
||||
env['items'].append(items)
|
||||
items = []
|
||||
|
||||
items.append(_format_opt(option))
|
||||
|
||||
env['items'].append(items)
|
||||
|
||||
file_path = ("%(target)s/%(package_name)s-%(cat)s.rst" %
|
||||
{'target': target, 'package_name': package_name,
|
||||
'cat': cat})
|
||||
tmpl_file = os.path.join(os.path.dirname(__file__),
|
||||
'templates/autohelp-category.rst.j2')
|
||||
_write_template(tmpl_file, file_path, env)
|
||||
|
||||
|
||||
def _write_files_by_group(package_name, options, target):
|
||||
for group in options.get_group_names():
|
||||
env = {
|
||||
'pkg': package_name,
|
||||
'group': group,
|
||||
'label': '-'.join([package_name, group]),
|
||||
'items': [],
|
||||
}
|
||||
|
||||
for option in options.get_group(group):
|
||||
env['items'].append(_format_opt(option))
|
||||
|
||||
file_path = ("%(target)s/%(package_name)s-%(group)s.rst" %
|
||||
{'target': target, 'package_name': package_name,
|
||||
'group': group})
|
||||
tmpl_file = os.path.join(os.path.dirname(__file__),
|
||||
'templates/autohelp-group.rst.j2')
|
||||
_write_template(tmpl_file, file_path, env)
|
||||
|
||||
|
||||
def _write_template(template_path, output_path, env):
|
||||
with open(template_path) as fd:
|
||||
template = jinja2.Template(fd.read(), trim_blocks=True)
|
||||
output = template.render(filename=output_path, **env)
|
||||
|
||||
with open(output_path, 'w') as fd:
|
||||
fd.write(output)
|
||||
|
||||
|
||||
def update_flagmappings(package_name, options, verbose=0):
|
||||
"""Update flagmappings file.
|
||||
|
||||
Update a flagmappings file, adding or removing entries as needed.
|
||||
This will create a new file $package_name.flagmappings.new with
|
||||
category information merged from the existing $package_name.flagmappings.
|
||||
"""
|
||||
if not _use_categories:
|
||||
print("This project does not use flagmappings. Nothing to update.")
|
||||
return
|
||||
|
||||
original_flags = {}
|
||||
try:
|
||||
with open(package_name + '.flagmappings') as f:
|
||||
for line in f:
|
||||
try:
|
||||
flag, category = line.split(' ', 1)
|
||||
except ValueError:
|
||||
flag = line.strip()
|
||||
category = "Unknown"
|
||||
original_flags.setdefault(flag, []).append(category.strip())
|
||||
except IOError:
|
||||
# If the flags file doesn't exist we'll create it
|
||||
pass
|
||||
|
||||
updated_flags = []
|
||||
for opt in options.get_option_names():
|
||||
if len(original_flags.get(opt, [])) == 1:
|
||||
updated_flags.append((opt, original_flags[opt][0]))
|
||||
continue
|
||||
|
||||
updated_flags.append((opt, 'Unknown'))
|
||||
|
||||
with open(package_name + '.flagmappings.new', 'w') as f:
|
||||
for flag, category in updated_flags:
|
||||
f.write(flag + ' ' + category + '\n')
|
||||
|
||||
if verbose >= 1:
|
||||
removed_flags = (set(original_flags.keys()) -
|
||||
set([x[0] for x in updated_flags]))
|
||||
added_flags = (set([x[0] for x in updated_flags]) -
|
||||
set(original_flags.keys()))
|
||||
|
||||
print("\nRemoved Flags\n")
|
||||
for line in sorted(removed_flags, OptionsCache._cmpopts):
|
||||
print(line)
|
||||
|
||||
print("\nAdded Flags\n")
|
||||
for line in sorted(added_flags, OptionsCache._cmpopts):
|
||||
print(line)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Manage flag files, to aid in updating documentation.',
|
||||
usage='%(prog)s <cmd> <package> [options]')
|
||||
parser.add_argument('subcommand',
|
||||
help='Action (update, rst, dump).',
|
||||
choices=['update', 'rst', 'dump'])
|
||||
parser.add_argument('package',
|
||||
help='Name of the top-level package.')
|
||||
parser.add_argument('-v', '--verbose',
|
||||
action='count',
|
||||
default=0,
|
||||
dest='verbose',
|
||||
required=False,)
|
||||
parser.add_argument('-i', '--input',
|
||||
dest='repos',
|
||||
help='Path to a python package in which options '
|
||||
'should be discoverd. Can be used multiple '
|
||||
'times.',
|
||||
required=False,
|
||||
type=str,
|
||||
action='append')
|
||||
parser.add_argument('-o', '--output',
|
||||
dest='target',
|
||||
help='Directory or file in which data will be saved.\n'
|
||||
'Defaults to ../../doc/common/tables/ '
|
||||
'for "rst".\n'
|
||||
'Defaults to stdout for "dump"',
|
||||
required=False,
|
||||
type=str,)
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.repos is None:
|
||||
args.repos = ['./sources/%s/%s' % args.package]
|
||||
|
||||
for repository in args.repos:
|
||||
package_name = os.path.basename(repository)
|
||||
base_path = os.path.dirname(repository)
|
||||
|
||||
sys.path.insert(0, base_path)
|
||||
try:
|
||||
__import__(package_name)
|
||||
except ImportError as e:
|
||||
if args.verbose >= 1:
|
||||
print(str(e))
|
||||
print("Failed to import: %s (%s)" % (package_name, e))
|
||||
|
||||
import_modules(base_path, package_name, verbose=args.verbose)
|
||||
sys.path.pop(0)
|
||||
|
||||
overrides = _get_overrides(package_name)
|
||||
options = OptionsCache(overrides, verbose=args.verbose)
|
||||
options.maybe_load_extensions(args.repos)
|
||||
|
||||
if args.verbose > 0:
|
||||
print("%s options imported from package %s." % (len(options),
|
||||
str(package_name)))
|
||||
|
||||
if args.subcommand == 'update':
|
||||
update_flagmappings(args.package, options, verbose=args.verbose)
|
||||
|
||||
elif args.subcommand == 'rst':
|
||||
write_files(args.package, options, args.target)
|
||||
|
||||
elif args.subcommand == 'dump':
|
||||
options.dump()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
@ -1,289 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
#
|
||||
# A collection of tools for working with flags from OpenStack
|
||||
# packages and documentation.
|
||||
#
|
||||
# For an example of usage, run this program with the -h switch.
|
||||
#
|
||||
import argparse
|
||||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import jinja2
|
||||
|
||||
|
||||
PROJECTS = ['aodh', 'ceilometer', 'cinder', 'glance', 'heat', 'ironic',
|
||||
'keystone', 'manila', 'neutron', 'nova', 'sahara', 'trove']
|
||||
MASTER_RELEASE = 'Ocata'
|
||||
CODENAME_TITLE = {'aodh': 'Alarming',
|
||||
'ceilometer': 'Telemetry',
|
||||
'cinder': 'Block Storage',
|
||||
'glance': 'Image service',
|
||||
'heat': 'Orchestration',
|
||||
'ironic': 'Bare Metal service',
|
||||
'keystone': 'Identity service',
|
||||
'manila': 'Shared File Systems service',
|
||||
'murano': 'Application Catalog service',
|
||||
'neutron': 'Networking',
|
||||
'nova': 'Compute',
|
||||
'sahara': 'Data Processing service',
|
||||
'senlin': 'Clustering service',
|
||||
'trove': 'Database service',
|
||||
'zaqar': 'Message service'}
|
||||
|
||||
|
||||
def setup_venv(projects, branch, novenvupdate):
|
||||
"""Setup a virtual environment for `branch`."""
|
||||
dirname = os.path.join('venv', branch.replace('/', '_'))
|
||||
if novenvupdate and os.path.exists(dirname):
|
||||
return
|
||||
if not os.path.exists('venv'):
|
||||
os.mkdir('venv')
|
||||
args = ["./autohelp-wrapper", "-b", branch, "-e", dirname, "setup"]
|
||||
args.extend(projects)
|
||||
if subprocess.call(args) != 0:
|
||||
print("Impossible to create the %s environment." % branch)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def _get_packages(project, branch):
|
||||
release = branch if '/' not in branch else branch.split('/')[1]
|
||||
packages = [project]
|
||||
try:
|
||||
with open('extra_repos/%s-%s.txt' % (project, release)) as f:
|
||||
packages.extend([p.strip() for p in f])
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return packages
|
||||
|
||||
|
||||
def get_options(project, branch):
|
||||
"""Get the list of known options for a project."""
|
||||
print("Working on %(project)s (%(branch)s)" % {'project': project,
|
||||
'branch': branch})
|
||||
# And run autohelp script to get a serialized dict of the discovered
|
||||
# options
|
||||
dirname = os.path.join('venv', branch.replace('/', '_'))
|
||||
args = ["./autohelp-wrapper", "-q", "-b", branch, "-e", dirname,
|
||||
"dump", project]
|
||||
|
||||
path = os.environ.get("PATH")
|
||||
bin_path = os.path.abspath(os.path.join(dirname, "bin"))
|
||||
path = "%s:%s" % (bin_path, path)
|
||||
serialized = subprocess.check_output(args,
|
||||
env={'VIRTUAL_ENV': dirname,
|
||||
'PATH': path})
|
||||
ret = pickle.loads(serialized)
|
||||
return ret
|
||||
|
||||
|
||||
def _cmpopts(x, y):
|
||||
"""Compare to option names.
|
||||
|
||||
The options can be of 2 forms: option_name or group/option_name. Options
|
||||
without a group always comes first. Options are sorted alphabetically
|
||||
inside a group.
|
||||
"""
|
||||
if '/' in x and '/' in y:
|
||||
prex = x[:x.find('/')]
|
||||
prey = y[:y.find('/')]
|
||||
if prex != prey:
|
||||
return cmp(prex, prey)
|
||||
return cmp(x, y)
|
||||
elif '/' in x:
|
||||
return 1
|
||||
elif '/' in y:
|
||||
return -1
|
||||
else:
|
||||
return cmp(x, y)
|
||||
|
||||
|
||||
def diff(old_list, new_list):
|
||||
"""Compare the old and new lists of options."""
|
||||
new_opts = []
|
||||
new_defaults = []
|
||||
deprecated_opts = []
|
||||
for name, (group, option) in new_list.items():
|
||||
# Find the new options
|
||||
if name not in old_list.viewkeys():
|
||||
new_opts.append(name)
|
||||
|
||||
# Find the options for which the default value has changed
|
||||
elif option['default'] != old_list[name][1]['default']:
|
||||
new_defaults.append(name)
|
||||
|
||||
# Find options that have been deprecated in the new release.
|
||||
# If an option name is a key in the old_list dict, it means that it
|
||||
# wasn't deprecated.
|
||||
|
||||
# Some options are deprecated, but not replaced with a new option.
|
||||
# These options usually contain 'DEPRECATED' in their help string.
|
||||
if 'DEPRECATED' in option['help']:
|
||||
deprecated_opts.append((name, None))
|
||||
|
||||
for deprecated in option['deprecated_opts']:
|
||||
# deprecated_opts is a list which always holds at least 1 invalid
|
||||
# dict. Forget it.
|
||||
if deprecated['name'] is None:
|
||||
continue
|
||||
|
||||
if deprecated['group'] in [None, 'DEFAULT']:
|
||||
full_name = deprecated['name']
|
||||
else:
|
||||
full_name = deprecated['group'] + '/' + deprecated['name']
|
||||
|
||||
if full_name in old_list.viewkeys():
|
||||
deprecated_opts.append((full_name, name))
|
||||
|
||||
return new_opts, new_defaults, deprecated_opts
|
||||
|
||||
|
||||
def format_option_name(name):
|
||||
"""Return a formatted string for the option path."""
|
||||
if name is None:
|
||||
return "None"
|
||||
|
||||
try:
|
||||
section, name = name.split('/')
|
||||
except ValueError:
|
||||
# name without a section ('log_dir')
|
||||
return "[DEFAULT] %s" % name
|
||||
|
||||
return "[%s] %s" % (section, name)
|
||||
|
||||
|
||||
def release_from_branch(branch):
|
||||
if branch == 'master':
|
||||
return MASTER_RELEASE
|
||||
else:
|
||||
return branch.replace('stable/', '').title()
|
||||
|
||||
|
||||
def get_env(project, new_branch, old_list, new_list):
|
||||
"""Generate the jinja2 environment for the defined branch and project."""
|
||||
new_opts, new_defaults, deprecated_opts = diff(old_list, new_list)
|
||||
release = release_from_branch(new_branch)
|
||||
|
||||
env = {
|
||||
'release': release,
|
||||
'project': project,
|
||||
'codename': CODENAME_TITLE[project],
|
||||
'new_opts': [],
|
||||
'new_defaults': [],
|
||||
'deprecated_opts': []
|
||||
}
|
||||
|
||||
# New options
|
||||
if new_opts:
|
||||
for name in sorted(new_opts, _cmpopts):
|
||||
opt = new_list[name][1]
|
||||
name = format_option_name(name)
|
||||
helptext = opt['help'].strip().replace('\n', ' ')
|
||||
helptext = ' '.join(helptext.split())
|
||||
cells = (("%(name)s = %(default)s" %
|
||||
{'name': name,
|
||||
'default': opt['default']}).strip(),
|
||||
"(%(type)s) %(help)s" % {'type': opt['type'],
|
||||
'help': helptext})
|
||||
env['new_opts'].append(cells)
|
||||
|
||||
# New defaults
|
||||
if new_defaults:
|
||||
for name in sorted(new_defaults, _cmpopts):
|
||||
old_default = old_list[name][1]['default']
|
||||
new_default = new_list[name][1]['default']
|
||||
if isinstance(old_default, list):
|
||||
old_default = ", ".join(old_default)
|
||||
if isinstance(new_default, list):
|
||||
new_default = ", ".join(new_default)
|
||||
name = format_option_name(name)
|
||||
cells = (name, old_default, new_default)
|
||||
env['new_defaults'].append(cells)
|
||||
|
||||
# Deprecated options
|
||||
if deprecated_opts:
|
||||
for deprecated, new in sorted(deprecated_opts, cmp=_cmpopts,
|
||||
key=lambda tup: tup[0]):
|
||||
deprecated = format_option_name(deprecated)
|
||||
new = format_option_name(new)
|
||||
env['deprecated_opts'].append((deprecated, new))
|
||||
|
||||
return env
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Generate a summary of configuration option changes.',
|
||||
usage='%(prog)s [options] <old_branch> <new_branch> [projects]')
|
||||
parser.add_argument('old_branch',
|
||||
help='Name of the old branch.')
|
||||
parser.add_argument('new_branch',
|
||||
help='Name of the new branch.')
|
||||
parser.add_argument('projects',
|
||||
help='List of projects to work on.',
|
||||
nargs='*',
|
||||
default=PROJECTS)
|
||||
parser.add_argument('-i', '--input',
|
||||
dest='sources',
|
||||
help='Path to a folder containing the git '
|
||||
'repositories.',
|
||||
required=False,
|
||||
default='./sources',
|
||||
type=str,)
|
||||
parser.add_argument('-o', '--output',
|
||||
dest='target',
|
||||
help='Directory or file in which data will be saved.\n'
|
||||
'Defaults to "."',
|
||||
required=False,
|
||||
default='.',
|
||||
type=str,)
|
||||
parser.add_argument('-n', '--no-venv-update',
|
||||
dest='novenvupdate',
|
||||
help='Don\'t update the virtual envs.',
|
||||
required=False,
|
||||
action='store_true',
|
||||
default=False,)
|
||||
args = parser.parse_args()
|
||||
|
||||
setup_venv(args.projects, args.old_branch, args.novenvupdate)
|
||||
setup_venv(args.projects, args.new_branch, args.novenvupdate)
|
||||
|
||||
for project in args.projects:
|
||||
old_list = get_options(project, args.old_branch)
|
||||
new_list = get_options(project, args.new_branch)
|
||||
|
||||
release = args.new_branch.replace('stable/', '')
|
||||
env = get_env(project, release, old_list, new_list)
|
||||
filename = ("%(project)s-conf-changes.rst" %
|
||||
{'project': project})
|
||||
tmpl_file = 'templates/changes.rst.j2'
|
||||
if not os.path.exists(args.target):
|
||||
os.makedirs(args.target)
|
||||
dest = os.path.join(args.target, filename)
|
||||
|
||||
with open(tmpl_file) as fd:
|
||||
template = jinja2.Template(fd.read(), trim_blocks=True)
|
||||
output = template.render(**env)
|
||||
|
||||
with open(dest, 'w') as fd:
|
||||
fd.write(output)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,5 +0,0 @@
|
||||
docutils
|
||||
jinja2
|
||||
lxml
|
||||
oslo.config
|
||||
oslo.i18n
|
@ -1,45 +0,0 @@
|
||||
..
|
||||
Warning: Do not edit this file. It is automatically generated from the
|
||||
software project's code and your changes will be overwritten.
|
||||
|
||||
The tool to generate this file lives in openstack-doc-tools repository.
|
||||
|
||||
Please make any changes needed in the code, then run the
|
||||
autogenerate-config-doc tool from the openstack-doc-tools repository, or
|
||||
ask for help on the documentation mailing list, IRC channel or meeting.
|
||||
|
||||
.. _{{ label }}:
|
||||
|
||||
.. list-table:: Description of {{ nice_cat }} configuration options
|
||||
:header-rows: 1
|
||||
:class: config-ref-table
|
||||
|
||||
* - Configuration option = Default value
|
||||
- Description
|
||||
{% for group in groups %}
|
||||
|
||||
* - **[{{ group }}]**
|
||||
-
|
||||
{% for item in items[loop.index0] %}
|
||||
|
||||
{% if item['default'] is equalto '' %}
|
||||
* - ``{{ item['name'] }}`` =
|
||||
{% else %}
|
||||
* - ``{{ item['name'] }}`` = ``{{ item['default'] }}``
|
||||
{% endif %}
|
||||
{% for paragraph in item['help'] %}
|
||||
|
||||
{% if loop.first %}
|
||||
- ({{ item['type'] }}) {{ paragraph }}
|
||||
{% else %}
|
||||
{{ paragraph }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for flagname, flagdesc in item['flags'] %}
|
||||
|
||||
- **{{ flagname }}**
|
||||
|
||||
{{ flagdesc }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
@ -1,40 +0,0 @@
|
||||
..
|
||||
Warning: Do not edit this file. It is automatically generated from the
|
||||
software project's code and your changes will be overwritten.
|
||||
|
||||
The tool to generate this file lives in openstack-doc-tools repository.
|
||||
|
||||
Please make any changes needed in the code, then run the
|
||||
autogenerate-config-doc tool from the openstack-doc-tools repository, or
|
||||
ask for help on the documentation mailing list, IRC channel or meeting.
|
||||
|
||||
.. _{{ label }}:
|
||||
|
||||
.. list-table:: Description of {{ group }} configuration options
|
||||
:header-rows: 1
|
||||
:class: config-ref-table
|
||||
|
||||
* - Configuration option = Default value
|
||||
- Description
|
||||
{% for item in items %}
|
||||
|
||||
{% if item['default'] is equalto '' %}
|
||||
* - ``{{ item['name'] }}`` =
|
||||
{% else %}
|
||||
* - ``{{ item['name'] }}`` = ``{{ item['default'] }}``
|
||||
{% endif %}
|
||||
{% for paragraph in item['help'] %}
|
||||
|
||||
{% if loop.first %}
|
||||
- ({{ item['type'] }}) {{ paragraph }}
|
||||
{% else %}
|
||||
{{ paragraph }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for flagname, flagdesc in item['flags'] %}
|
||||
|
||||
- **{{ flagname }}**
|
||||
|
||||
{{ flagdesc }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
@ -1,61 +0,0 @@
|
||||
New, updated, and deprecated options in {{ release }} for {{ codename }}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{{ '~' * release|length }}~~~~~{{ '~' * codename|length }}
|
||||
|
||||
..
|
||||
Warning: Do not edit this file. It is automatically generated and your
|
||||
changes will be overwritten. The tool to do so lives in the
|
||||
openstack-doc-tools repository.
|
||||
|
||||
{% if new_opts %}
|
||||
.. list-table:: New options
|
||||
:header-rows: 1
|
||||
:class: config-ref-table
|
||||
|
||||
* - Option = default value
|
||||
- (Type) Help string
|
||||
{% for cells in new_opts %}
|
||||
* - ``{{ cells[0] }}``
|
||||
- {{ cells[1] }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if new_defaults %}
|
||||
.. list-table:: New default values
|
||||
:header-rows: 1
|
||||
:class: config-ref-table
|
||||
|
||||
* - Option
|
||||
- Previous default value
|
||||
- New default value
|
||||
{% for cells in new_defaults %}
|
||||
* - ``{{ cells[0] }}``
|
||||
{% if cells[1] is equalto '' %}
|
||||
-
|
||||
{% else %}
|
||||
- ``{{ cells[1] }}``
|
||||
{% endif %}
|
||||
{% if cells[2] is equalto '' %}
|
||||
-
|
||||
{% else %}
|
||||
- ``{{ cells[2] }}``
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if deprecated_opts %}
|
||||
.. list-table:: Deprecated options
|
||||
:header-rows: 1
|
||||
:class: config-ref-table
|
||||
|
||||
* - Deprecated option
|
||||
- New Option
|
||||
{% for cells in deprecated_opts %}
|
||||
* - ``{{ cells[0] }}``
|
||||
- ``{{ cells[1] }}``
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if not new_opts and not new_defaults and not deprecated_opts %}
|
||||
There are no new, updated, and deprecated options
|
||||
in {{ release }} for {{ codename }}.
|
||||
{% endif %}
|
@ -1,113 +0,0 @@
|
||||
#!/bin/bash -e
|
||||
#
|
||||
# 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.
|
||||
|
||||
DIRECTORY=$1
|
||||
|
||||
if [ -z "$DIRECTORY" ] ; then
|
||||
echo "usage $0 DIRECTORY options"
|
||||
echo "Options are:"
|
||||
echo "--tag TAG: Use given tag for building"
|
||||
echo "--target TARGET: Copy files to publish-docs/$TARGET"
|
||||
echo "--build BUILD: Name of build directory"
|
||||
echo "--linkcheck: Check validity of links instead of building"
|
||||
echo "--pdf: PDF file generation"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
TARGET=""
|
||||
TAG=""
|
||||
TAG_OPT=""
|
||||
BUILD=""
|
||||
LINKCHECK=""
|
||||
PDF=""
|
||||
|
||||
while [[ $# > 0 ]] ; do
|
||||
option="$1"
|
||||
case $option in
|
||||
--build)
|
||||
BUILD="$2"
|
||||
shift
|
||||
;;
|
||||
--linkcheck)
|
||||
LINKCHECK=1
|
||||
;;
|
||||
--tag)
|
||||
TAG="$2"
|
||||
TAG_OPT="-t $2"
|
||||
shift
|
||||
;;
|
||||
--target)
|
||||
TARGET="$2"
|
||||
shift
|
||||
;;
|
||||
--pdf)
|
||||
PDF=1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
|
||||
if [ -z "$BUILD" ] ; then
|
||||
if [ -z "$TAG" ] ; then
|
||||
BUILD_DIR="$DIRECTORY/build/html"
|
||||
BUILD_DIR_PDF="$DIRECTORY/build/pdf"
|
||||
else
|
||||
BUILD_DIR="$DIRECTORY/build-${TAG}/html"
|
||||
BUILD_DIR_PDF="$DIRECTORY/build-${TAG}/pdf"
|
||||
fi
|
||||
else
|
||||
BUILD_DIR="$DIRECTORY/$BUILD/html"
|
||||
BUILD_DIR_PDF="$DIRECTORY/$BUILD/pdf"
|
||||
fi
|
||||
|
||||
DOCTREES="${BUILD_DIR}.doctrees"
|
||||
|
||||
if [ -z "$TAG" ] ; then
|
||||
echo "Checking $DIRECTORY..."
|
||||
else
|
||||
echo "Checking $DIRECTORY with tag $TAG..."
|
||||
fi
|
||||
|
||||
if [ "$LINKCHECK" = "1" ] ; then
|
||||
# Show sphinx-build invocation for easy reproduction
|
||||
set -x
|
||||
sphinx-build -E -W -d $DOCTREES -b linkcheck \
|
||||
$TAG_OPT $DIRECTORY/source $BUILD_DIR
|
||||
set +x
|
||||
else
|
||||
# Show sphinx-build invocation for easy reproduction
|
||||
set -x
|
||||
sphinx-build -E -W -d $DOCTREES -b html \
|
||||
$TAG_OPT $DIRECTORY/source $BUILD_DIR
|
||||
set +x
|
||||
|
||||
# PDF generation
|
||||
if [ "$PDF" = "1" ] ; then
|
||||
set -x
|
||||
sphinx-build -E -W -d $DOCTREES -b latex \
|
||||
$TAG_OPT $DIRECTORY/source $BUILD_DIR_PDF
|
||||
make -C $BUILD_DIR_PDF
|
||||
cp $BUILD_DIR_PDF/*.pdf $BUILD_DIR/
|
||||
set +x
|
||||
fi
|
||||
|
||||
# Copy RST (and PDF)
|
||||
if [ "$TARGET" != "" ] ; then
|
||||
mkdir -p publish-docs/$TARGET
|
||||
rsync -a $BUILD_DIR/ publish-docs/$TARGET/
|
||||
# Remove unneeded build artefact
|
||||
rm -f publish-docs/$TARGET/.buildinfo
|
||||
fi
|
||||
fi
|
@ -1,337 +0,0 @@
|
||||
#!/bin/bash -xe
|
||||
|
||||
# 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.
|
||||
|
||||
INSTALL_TAGS="obs rdo ubuntu debian debconf"
|
||||
FIRSTAPP_TAGS="libcloud dotnet fog openstacksdk pkgcloud shade"
|
||||
|
||||
# This marker is needed for Infra publishing and needs to go into the
|
||||
# root directory of each translated manual as file ".root-marker".
|
||||
MARKER_TEXT="Project: $ZUUL_PROJECT Ref: $ZUUL_REFNAME Build: $ZUUL_UUID Revision: $ZUUL_NEWREV"
|
||||
|
||||
function build_rst {
|
||||
language=$1
|
||||
book=$2
|
||||
local ret
|
||||
|
||||
# First build all the single po files
|
||||
# Note that we need to run inside a venv since the venv we are run in
|
||||
# uses SitePackages=True and we have to install Sphinx in the venv
|
||||
# together with openstackdocstheme. With SitePackages, the global Sphinx
|
||||
# is used and that will not work with a local openstackdocstheme installed.
|
||||
TAG=""
|
||||
# We need to extract all strings, so add all supported tags
|
||||
if [ ${book} = "firstapp" ] ; then
|
||||
TAG="-t libcloud -t fog -t dotnet -t openstacksdk -t pkgcloud -t shade"
|
||||
fi
|
||||
if [ ${book} = "install-guide" ] ; then
|
||||
TAG="-t obs -t rdo -t ubuntu -t debian"
|
||||
fi
|
||||
|
||||
COMMON="common"
|
||||
LOCALE_DIR="${DOC_DIR}${book}/source/locale/"
|
||||
COMMON_DIR="${DOC_DIR}${COMMON}/source/locale/"
|
||||
|
||||
tox -evenv -- sphinx-build -q -E -W -b gettext $TAG \
|
||||
${DOC_DIR}${book}/source/ ${LOCALE_DIR}
|
||||
|
||||
|
||||
# Merge the common po file
|
||||
if [[ -e ${COMMON_DIR}${language}/LC_MESSAGES/${COMMON}.po ]] ; then
|
||||
msgcat --use-first -o ${LOCALE_DIR}${language}/${book}.po \
|
||||
${LOCALE_DIR}${language}/LC_MESSAGES/${book}.po \
|
||||
${COMMON_DIR}${language}/LC_MESSAGES/${COMMON}.po
|
||||
mv -f ${LOCALE_DIR}${language}/${book}.po \
|
||||
${LOCALE_DIR}${language}/LC_MESSAGES/${book}.po
|
||||
fi
|
||||
# Now run msgmerge on all files
|
||||
for f in ${LOCALE_DIR}*.pot ; do
|
||||
# Skip the master file
|
||||
if [ $f = "${LOCALE_DIR}${book}.pot" ] ; then
|
||||
continue
|
||||
fi
|
||||
bf=$(basename $f)
|
||||
# Remove .pot
|
||||
bfname=${bf%.pot}
|
||||
msgmerge --silent \
|
||||
-o ${LOCALE_DIR}${language}/LC_MESSAGES/${bfname}.po \
|
||||
${LOCALE_DIR}${language}/LC_MESSAGES/${book}.po \
|
||||
${LOCALE_DIR}${bf}
|
||||
msgfmt ${LOCALE_DIR}${language}/LC_MESSAGES/${bfname}.po \
|
||||
-o ${LOCALE_DIR}${language}/LC_MESSAGES/${bfname}.mo
|
||||
done
|
||||
|
||||
# Set the bug project to I18n project
|
||||
set +e
|
||||
grep 'bug_project' ${DOC_DIR}${book}/source/conf.py > /dev/null
|
||||
ret=$?
|
||||
set -e
|
||||
if [ "$ret" -eq 0 ] ; then
|
||||
# Replace the existing "bug_project" html context
|
||||
sed -i -e \
|
||||
's/"bug_project" *: *[^ ,}]*/"bug_project": "openstack-i18n"/' \
|
||||
${DOC_DIR}${book}/source/conf.py
|
||||
else
|
||||
# Add the "bug_project" html context
|
||||
sed -i -e \
|
||||
's/html_context *= *{/html_context = { \
|
||||
"bug_project": "openstack-i18n", /' \
|
||||
${DOC_DIR}${book}/source/conf.py
|
||||
fi
|
||||
|
||||
# Build all books
|
||||
if [ ${book} = "firstapp" ] ; then
|
||||
# Firstapp has several variations, build all of them
|
||||
for tag in $FIRSTAPP_TAGS ; do
|
||||
BUILD_DIR="${DOC_DIR}${book}/build-${tag}/html"
|
||||
DOCTREES="${BUILD_DIR}.doctrees"
|
||||
tox -evenv -- sphinx-build -q -E \
|
||||
-t $tag -D language=${language} \
|
||||
-d ${DOCTREES} \
|
||||
${DOC_DIR}${book}/source/ \
|
||||
${BUILD_DIR}
|
||||
PUBLISH_DIR=publish-docs/${language}/${book}-${tag}
|
||||
mkdir -p ${PUBLISH_DIR}
|
||||
rsync -a ${DOC_DIR}${book}/build-${tag}/html/ ${PUBLISH_DIR}
|
||||
echo $MARKER_TEXT > ${PUBLISH_DIR}/.root-marker
|
||||
done
|
||||
elif [ ${book} = "install-guide" ] ; then
|
||||
# Install Guide has several variations, build all of them
|
||||
INDEX=${DOC_DIR}${book}/source/index.rst
|
||||
|
||||
# For translation work, we should have only one index file,
|
||||
# because our tools generate translation resources from
|
||||
# only one index file.
|
||||
# Therefore, this tool uses one combined index file
|
||||
# while processing title and toctree for each distribution.
|
||||
|
||||
# Save and restore the index file
|
||||
cp -f ${INDEX} ${INDEX}.save
|
||||
trap "mv -f ${INDEX}.save ${INDEX}" EXIT
|
||||
|
||||
for tag in $INSTALL_TAGS; do
|
||||
if [[ "$tag" == "debconf" ]]; then
|
||||
# Not all branches have this directory
|
||||
if [[ -d ${DOC_DIR}${book}-${tag}/source ]] ; then
|
||||
# Build the guide with debconf
|
||||
# To use debian only contents, use "debian" tag.
|
||||
BUILD_DIR="${DOC_DIR}${book}-${tag}/build-${tag}/html"
|
||||
DOCTREES="${BUILD_DIR}.doctrees"
|
||||
tox -evenv -- sphinx-build -q -E -t debian \
|
||||
-D language=${language} \
|
||||
-d ${DOCTREES} \
|
||||
${DOC_DIR}${book}-${tag}/source/ \
|
||||
${BUILD_DIR}
|
||||
PUBLISH_DIR=publish-docs/${language}/${book}-${tag}
|
||||
mkdir -p ${PUBLISH_DIR}
|
||||
rsync -a ${DOC_DIR}${book}-${tag}/build-${tag}/html/ \
|
||||
${PUBLISH_DIR}
|
||||
echo $MARKER_TEXT > ${PUBLISH_DIR}/.root-marker
|
||||
fi
|
||||
else
|
||||
##
|
||||
# Because Sphinx uses the first heading as title regardless of
|
||||
# only directive, replace title directive with the proper title
|
||||
# for each distribution to set the title explicitly.
|
||||
title=$(grep -m 1 -A 5 "^.. only:: ${tag}" ${INDEX} | \
|
||||
sed -n 4p | sed -e 's/^ *//g')
|
||||
sed -i -e "s/\.\. title::.*/.. title:: ${title}/" ${INDEX}
|
||||
|
||||
##
|
||||
# Sphinx builds the navigation before processing directives,
|
||||
# so the conditional toctree does not work.
|
||||
# We need to prepare toctree depending on distribution
|
||||
# only with one toctree before exectuing sphinx-build.
|
||||
|
||||
# Build the guide
|
||||
BUILD_DIR="${DOC_DIR}${book}/build-${tag}/html"
|
||||
DOCTREES="${BUILD_DIR}.doctrees"
|
||||
tox -evenv -- sphinx-build -q -E -t $tag \
|
||||
-D language=${language} \
|
||||
-d ${DOCTREES} \
|
||||
${DOC_DIR}${book}/source/ \
|
||||
${BUILD_DIR}
|
||||
PUBLISH_DIR=publish-docs/${language}/${book}-${tag}
|
||||
mkdir -p ${PUBLISH_DIR}
|
||||
rsync -a ${DOC_DIR}${book}/build-${tag}/html/ \
|
||||
${PUBLISH_DIR}
|
||||
echo $MARKER_TEXT > ${PUBLISH_DIR}/.root-marker
|
||||
fi
|
||||
done
|
||||
else
|
||||
BUILD_DIR="${DOC_DIR}${book}/build/html"
|
||||
DOCTREES="${BUILD_DIR}.doctrees"
|
||||
tox -evenv -- sphinx-build \
|
||||
-q -E -D language=${language} \
|
||||
-d ${DOCTREES} \
|
||||
${DOC_DIR}${book}/source/ \
|
||||
${BUILD_DIR}
|
||||
PUBLISH_DIR=publish-docs/${language}/${book}/
|
||||
mkdir -p ${PUBLISH_DIR}
|
||||
rsync -a ${DOC_DIR}${book}/build/html/ ${PUBLISH_DIR}
|
||||
echo $MARKER_TEXT > ${PUBLISH_DIR}/.root-marker
|
||||
fi
|
||||
# Remove newly created files
|
||||
git clean -f -q ${LOCALE_DIR}${language}/LC_MESSAGES/*.po
|
||||
git clean -f -x -q ${LOCALE_DIR}${language}/LC_MESSAGES/*.mo
|
||||
git clean -f -q ${LOCALE_DIR}*.pot
|
||||
# Revert changes to po file
|
||||
git reset -q ${LOCALE_DIR}${language}/LC_MESSAGES/${book}.po
|
||||
git checkout -- ${LOCALE_DIR}${language}/LC_MESSAGES/${book}.po
|
||||
# Revert changes to conf.py
|
||||
git reset -q ${DOC_DIR}${book}/source/conf.py
|
||||
git checkout -- ${DOC_DIR}${book}/source/conf.py
|
||||
}
|
||||
|
||||
|
||||
function test_language {
|
||||
language=$1
|
||||
|
||||
echo
|
||||
echo "Building for language $language"
|
||||
echo
|
||||
|
||||
args=("-v")
|
||||
if [[ $PURPOSE -eq "publish" ]]; then
|
||||
args+=("--publish")
|
||||
fi
|
||||
args+=("--check-build" "-l $language")
|
||||
for book in ${BOOKS["$language"]}; do
|
||||
if [ ${SPECIAL_BOOKS[$book]+_} ] ; then
|
||||
if [ ${SPECIAL_BOOKS[$book]} = "RST" ] ; then
|
||||
echo "Building translated RST book $book for $language"
|
||||
build_rst $language $book
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
function handle_draft_language {
|
||||
language=$1
|
||||
|
||||
echo
|
||||
echo "Moving drafts for language $language"
|
||||
echo
|
||||
|
||||
mkdir -p publish-docs/draft/$language
|
||||
for book in ${DRAFTS["$language"]}; do
|
||||
case "${book}" in
|
||||
config-reference)
|
||||
mv publish-docs/$language/draft/$book \
|
||||
publish-docs/draft/$language/$book
|
||||
rmdir --ignore-fail-on-non-empty publish-docs/$language/draft
|
||||
;;
|
||||
firstapp)
|
||||
for tag in $FIRSTAPP_TAGS; do
|
||||
mv publish-docs/$language/$book-${tag} \
|
||||
publish-docs/draft/$language/$book-${tag}
|
||||
done
|
||||
rmdir --ignore-fail-on-non-empty publish-docs/$language/
|
||||
;;
|
||||
install-guide)
|
||||
for tag in $INSTALL_TAGS ; do
|
||||
# Not all tags might be build on all branches
|
||||
if [[ -d publish-docs/$language/$book-${tag} ]] ; then
|
||||
mv publish-docs/$language/$book-${tag} \
|
||||
publish-docs/draft/$language/$book-${tag}
|
||||
fi
|
||||
done
|
||||
rmdir --ignore-fail-on-non-empty publish-docs/$language/
|
||||
;;
|
||||
*)
|
||||
mv publish-docs/$language/$book \
|
||||
publish-docs/draft/$language/$book
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
function usage {
|
||||
echo "usage: $0 CONF_FILE PURPOSE LANGUAGE1 LANGUAGE2 ..."
|
||||
echo
|
||||
echo "CONF_FILE is the path to the configuration file."
|
||||
echo
|
||||
echo "PURPOSE is either 'test' or 'publish'."
|
||||
echo
|
||||
echo "LANGUAGE is either 'all' or 'LANG'."
|
||||
echo "LANG is a language code like 'fr' or 'ja'."
|
||||
}
|
||||
|
||||
# Declare in case it's not in the file
|
||||
declare -A SPECIAL_BOOKS
|
||||
declare -A DRAFTS
|
||||
CONF_FILE=$1
|
||||
shift
|
||||
|
||||
if [[ -z $CONF_FILE ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -e $CONF_FILE ]]; then
|
||||
echo "Error: the configuration file '$CONF_FILE' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source $CONF_FILE
|
||||
|
||||
if [[ -z $(declare -p BOOKS 2> /dev/null | grep 'declare -A BOOKS') || \
|
||||
-z $(declare -p DIRECTORIES 2> /dev/null | \
|
||||
grep 'declare -A DIRECTORIES') || \
|
||||
-z $DOC_DIR ]]; then
|
||||
echo "Error: the configuration file '$CONF_FILE' is invalid"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$1" in
|
||||
test|publish)
|
||||
PURPOSE=$1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for language in "$@" ; do
|
||||
case "$language" in
|
||||
all)
|
||||
for language in "${!BOOKS[@]}"; do
|
||||
test_language $language
|
||||
done
|
||||
# Move draft language guides
|
||||
for language in "${!DRAFTS[@]}"; do
|
||||
handle_draft_language $language
|
||||
done
|
||||
;;
|
||||
*)
|
||||
if [[ -n ${BOOKS[$language]} ]]; then
|
||||
test_language $language
|
||||
if [ ${DRAFTS["${language}"]+_} ] ; then
|
||||
handle_draft_language $language
|
||||
fi
|
||||
else
|
||||
echo "Error: language $language not handled"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
exit 0
|
@ -1,36 +0,0 @@
|
||||
# Example configuration for the languages 'ja' and 'fr'.
|
||||
|
||||
# Directories to set up
|
||||
declare -A DIRECTORIES=(
|
||||
["ja"]="common glossary high-availability-guide image-guide install-guide user-guide"
|
||||
["fr"]="common glossary user-guide"
|
||||
)
|
||||
|
||||
# Books to build
|
||||
declare -A BOOKS=(
|
||||
["ja"]="high-availability-guide image-guide install-guide user-guide"
|
||||
["fr"]="user-guide"
|
||||
)
|
||||
|
||||
# draft books
|
||||
declare -A DRAFTS=(
|
||||
["fr"]="image-guide"
|
||||
["ja"]="install-guide"
|
||||
["pt_BR"]="install-guide"
|
||||
["zh_CN"]="install-guide"
|
||||
)
|
||||
|
||||
# Where does the top-level pom live?
|
||||
# Set to empty to not copy it.
|
||||
POM_FILE=doc/pom.xml
|
||||
|
||||
# Location of doc dir
|
||||
DOC_DIR="doc/"
|
||||
|
||||
# Books with special handling
|
||||
# Values need to match content in project-config/jenkins/scripts/common_translation_update.sh
|
||||
declare -A SPECIAL_BOOKS
|
||||
SPECIAL_BOOKS=(
|
||||
["user-guides"]="RST"
|
||||
["networking-guide"]="skip"
|
||||
)
|
@ -1,89 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
if [[ $# -ne 1 ]]; then
|
||||
echo "usage: $0 PROJECT"
|
||||
echo
|
||||
echo "PROJECT = something like nova, neutron or glance"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
project=$1
|
||||
|
||||
# checks command exist or not
|
||||
function does_exist {
|
||||
which $@ > /dev/null 2>&1
|
||||
local status=$?
|
||||
if [[ $status -ne 0 ]]; then
|
||||
echo "error: $1 not installed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
does_exist virtualenv
|
||||
does_exist pip
|
||||
does_exist git
|
||||
|
||||
if [[ ! -e $HOME/.gitconfig ]]; then
|
||||
echo "note: ~/.gitconfig does not exist"
|
||||
fi
|
||||
|
||||
if [[ ! -e .venv ]]; then
|
||||
virtualenv -p python2.7 .venv
|
||||
fi
|
||||
|
||||
source .venv/bin/activate
|
||||
|
||||
pip install --upgrade openstack-doc-tools
|
||||
pip install --upgrade pbr
|
||||
|
||||
# Cliff can optionally output HTML - if cliff-tablib is installed.
|
||||
pip install --upgrade cliff-tablib
|
||||
|
||||
# OSProfiler is an OpenStack cross-project profiling library.
|
||||
pip install --upgrade osprofiler
|
||||
|
||||
if [[ $project == 'aodh' ]]; then
|
||||
pip install --upgrade ${project}client
|
||||
elif [[ $project == 'gnocchi' ]]; then
|
||||
pip install --upgrade ${project}client
|
||||
else
|
||||
pip install --upgrade python-${project}client
|
||||
fi
|
||||
|
||||
rm -rf output
|
||||
mkdir output
|
||||
|
||||
# Work around until python-cinderclient use 3.latest as a default
|
||||
OS_VOLUME_API_VERSION=3.latest \
|
||||
openstack-auto-commands --output-dir output $project
|
||||
|
||||
if [[ ! -e openstack-manuals ]]; then
|
||||
git clone git://git.openstack.org/openstack/openstack-manuals
|
||||
fi
|
||||
|
||||
cd openstack-manuals
|
||||
|
||||
( git remote -v | grep -q gerrit ) || git review -s
|
||||
|
||||
git checkout master
|
||||
git pull
|
||||
branch=cli-reference
|
||||
git branch --list $branch && git branch -D $branch
|
||||
git checkout -b $branch
|
||||
mv ../output/${project}.rst "doc/cli-reference/source"
|
||||
rm -rf ../output
|
||||
version=$($project --version 2>&1)
|
||||
version=${version##*\)}
|
||||
git commit -a -m "[cli-ref] Update python-${project}client to ${version##* }"
|
@ -1,9 +0,0 @@
|
||||
# This is a cross-platform list tracking distribution packages needed by tests;
|
||||
# see http://docs.openstack.org/infra/bindep/ for additional information.
|
||||
|
||||
libssl-dev [platform:dpkg]
|
||||
openssl-devel [platform:rpm]
|
||||
python-dev [platform:dpkg]
|
||||
python3-all [platform:dpkg !platform:ubuntu-precise]
|
||||
python3-all-dev [platform:dpkg !platform:ubuntu-precise]
|
||||
python3-devel [platform:fedora]
|
@ -1,72 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""A script to prettify HTML and XML syntax.
|
||||
|
||||
Some examples of the prettified syntax are available
|
||||
in the following changes:
|
||||
|
||||
* https://review.openstack.org/#/c/98652/
|
||||
* https://review.openstack.org/#/c/98653/
|
||||
* https://review.openstack.org/#/c/98655/
|
||||
"""
|
||||
|
||||
# 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 argparse
|
||||
import sys
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def parse_command_line_arguments():
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--write-changes", action="store_true", default=False,
|
||||
help="Write prettified XML or HTML syntax "
|
||||
"back to file.")
|
||||
parser.add_argument("file", type=str, default=None,
|
||||
help="A XML or HTML File to prettify.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for this script."""
|
||||
|
||||
args = parse_command_line_arguments()
|
||||
|
||||
try:
|
||||
soup = BeautifulSoup(open(args.file))
|
||||
except IOError as exception:
|
||||
print("ERROR: File '%s' could not be parsed: %s"
|
||||
% (args.file, exception))
|
||||
return 1
|
||||
|
||||
if args.write_changes:
|
||||
try:
|
||||
with open(args.file, 'wb') as output:
|
||||
prettified = soup.prettify(encoding="utf8")
|
||||
output.write(prettified)
|
||||
except IOError as exception:
|
||||
print("ERROR: File '%s' could not be written: %s"
|
||||
% (args.file, exception))
|
||||
return 1
|
||||
else:
|
||||
prettified = soup.prettify(encoding="utf8")
|
||||
print(prettified)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
@ -1,24 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
## copyright: B1 Systems GmbH <info@b1-systems.de>, 2013.
|
||||
## author: Christian Berendt <berendt@b1-systems.de>, 2013.
|
||||
|
||||
# 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.
|
||||
|
||||
# Call ./tools/cleanup/remove_trailing_whitespaces.sh in the
|
||||
# root of openstack-manuals.
|
||||
|
||||
files=$(find doc -name *.xml -not -name pom.xml)
|
||||
for file in $files; do
|
||||
sed -i -e 's/[[:space:]]*$//' $file
|
||||
done
|
@ -1,69 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# copyright: B1 Systems GmbH <info@b1-systems.de>, 2013.
|
||||
|
||||
# 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.
|
||||
|
||||
# Call ./tools/cleanup/remove_unnecessary_spaces.py in the
|
||||
# root of openstack-manuals.
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
# should be the same like in tools/validate.py
|
||||
FILE_EXCEPTIONS = ['ha-guide-docinfo.xml',
|
||||
'bk001-ch003-associate-general.xml']
|
||||
|
||||
elements = [
|
||||
'listitem',
|
||||
'para',
|
||||
'td',
|
||||
'th',
|
||||
'command',
|
||||
'literal',
|
||||
'title',
|
||||
'caption',
|
||||
'filename',
|
||||
'userinput',
|
||||
'programlisting'
|
||||
]
|
||||
|
||||
checks = []
|
||||
for element in elements:
|
||||
checks.append(re.compile("(.*<%s>)\s+([\w\-().:!?{}\[\]]+.*\n)"
|
||||
% element)),
|
||||
checks.append(re.compile("(.*[\w\-().:!?{}\[\]]+)\s+(<\/%s>.*\n)"
|
||||
% element))
|
||||
|
||||
for root, dirs, files in os.walk('doc/'):
|
||||
for f in files:
|
||||
if (not (f.endswith('.xml') and
|
||||
f != 'pom.xml' and
|
||||
f not in FILE_EXCEPTIONS)):
|
||||
continue
|
||||
docfile = os.path.abspath(os.path.join(root, f))
|
||||
tmpfile = tempfile.mkstemp()
|
||||
tmpfd = os.fdopen(tmpfile[0], "w")
|
||||
match = False
|
||||
for line in open(docfile, 'r'):
|
||||
for check in checks:
|
||||
if check.match(line):
|
||||
line = check.sub(r"\1\2", line)
|
||||
match = True
|
||||
tmpfd.write(line)
|
||||
tmpfd.close()
|
||||
if match:
|
||||
shutil.copyfile(tmpfile[1], docfile)
|
||||
os.unlink(tmpfile[1])
|
@ -1,69 +0,0 @@
|
||||
# retf.py
|
||||
|
||||
This script applies a set of regular expressions onto a set of files
|
||||
to automatically identify and fix typographical errors.
|
||||
|
||||
## What does RETF mean?
|
||||
|
||||
RETF means RegExTypoFix or Regular Expression Typographical error Fixer
|
||||
and is a set of regular expressions to find and fix common misspellings
|
||||
and grammatical errors.
|
||||
|
||||
The regular expressions are available at
|
||||
https://en.wikipedia.org/wiki/Wikipedia:AutoWikiBrowser/Typos.
|
||||
|
||||
## Usage
|
||||
|
||||
There are two ways to define the set of files. First you can simply add
|
||||
single files using the parameter ```--file```.
|
||||
|
||||
```$ ./retf.py --file path/to/file1 path/to/file2 path/to/file3```
|
||||
|
||||
Also you can specify paths using the parameter ```--path``` that should be
|
||||
scanned for files.
|
||||
|
||||
```$ ./retf.py --path path/with/files/1 path/with/files/2```
|
||||
|
||||
To not use all files inside the specified paths it's possible to filter
|
||||
by the file extension.
|
||||
|
||||
```$ ./retf.py --path path/with/files --extension xml txt rst```
|
||||
|
||||
It's possible to use the parameters ```--path``` and ```--file``` together.
|
||||
|
||||
By default the script will only check for findings in all specified files.
|
||||
|
||||
To automatically write back resolved findings add the parameter
|
||||
```--write-changes```. Findings will then be written to a copy with
|
||||
the ending ```.retf```.
|
||||
|
||||
To fix findings directly in the files add the parameter
|
||||
```--in-place```. Findings will than be fixed directly in the files. A backup file
|
||||
with the ending ```.orig``` will be created. To disable backups add the
|
||||
paramter ```--no-backup```.
|
||||
|
||||
To only check if there are findings inside the defined set of files add
|
||||
|
||||
To download the latest RETF rules from Wikipedia use the parameter ```--download```.
|
||||
|
||||
## Needed Python modules
|
||||
|
||||
* beautifulsoup4 / bs4 (https://pypi.python.org/pypi/beautifulsoup4)
|
||||
* glob2 (https://pypi.python.org/pypi/glob2)
|
||||
* pyyaml (https://pypi.python.org/pypi/pyaml)
|
||||
* regex (https://pypi.python.org/pypi/regex)
|
||||
* six (https://pypi.python.org/pypi/six)
|
||||
|
||||
To install the needed modules you can use pip or the package management system included
|
||||
in your distribution. When using the package management system maybe the name of the
|
||||
packages differ. When using pip it's maybe necessary to install some development packages.
|
||||
For example on Ubuntu 14.04 LTS you have to install ```libyaml-dev``` for ```pyyaml```
|
||||
and ```python-dev``` for ```regex```.
|
||||
|
||||
```
|
||||
$ pip install beautifulsoup4
|
||||
$ pip install glob2
|
||||
$ pip install pyyaml
|
||||
$ pip install regex
|
||||
$ pip install six
|
||||
```
|
@ -1 +0,0 @@
|
||||
---
|
@ -1,307 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""This script applies a set of regular expressions onto a set of files
|
||||
to automatically identify and fix typographical errors.
|
||||
"""
|
||||
|
||||
# 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.
|
||||
|
||||
# Based on the idea of 'Topy' written by Marti Raudsepp <marti@juffo.org>.
|
||||
# Topy is available on Github at https://github.com/intgr/topy.
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from six.moves.urllib import error as urlerr
|
||||
from six.moves.urllib import request as urlreq
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import glob2
|
||||
import regex
|
||||
import six
|
||||
import yaml
|
||||
|
||||
|
||||
class DownloadRetfListingFailed(Exception):
|
||||
"""Exception for failed downloads of the RETF listing.
|
||||
|
||||
Exception will be raised when the download of the RETF
|
||||
listing failed or the destination file could not be written.
|
||||
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def download_listing(dest):
|
||||
"""Download the latest RETF listing from Wikipedia."""
|
||||
logger = logging.getLogger('retf')
|
||||
try:
|
||||
url = ('https://en.wikipedia.org/wiki/Wikipedia:AutoWikiBrowser/'
|
||||
'Typos?action=raw')
|
||||
logger.debug("Downloading latest RETF listing from %s into %s.",
|
||||
url, dest)
|
||||
response = urlreq.urlopen(url)
|
||||
data = response.read()
|
||||
logger.info("Downloading latest RETF listing from %s succeeded.", url)
|
||||
except urlerr.HTTPError as ex:
|
||||
raise DownloadRetfListingFailed(six.text_type(ex))
|
||||
except urlerr.URLError as ex:
|
||||
raise DownloadRetfListingFailed(six.text_type(ex))
|
||||
|
||||
try:
|
||||
with open(dest, 'w+') as write:
|
||||
write.write(data)
|
||||
logger.info("Writing RETF listing to file %s succeeded.", dest)
|
||||
except IOError as ex:
|
||||
raise DownloadRetfListingFailed(six.text_type(ex))
|
||||
|
||||
|
||||
def soupify_listing(src):
|
||||
"""Parse a RETF listing."""
|
||||
return BeautifulSoup(open(src))
|
||||
|
||||
|
||||
def generate_listing(src):
|
||||
"""Compile all regular expressions in a RETF listing."""
|
||||
logger = logging.getLogger('retf')
|
||||
result = []
|
||||
|
||||
soup = soupify_listing(src)
|
||||
|
||||
for typo in soup.findAll('typo'):
|
||||
try:
|
||||
word = typo.attrs.get('word').encode('utf8')
|
||||
find = typo.attrs.get('find').encode('utf8')
|
||||
replace = typo.attrs.get('replace').encode('utf8')
|
||||
replace = replace.replace(b'$', b'\\')
|
||||
except AttributeError:
|
||||
continue
|
||||
|
||||
# pylint: disable=W0703
|
||||
try:
|
||||
logger.debug("Compiling regular expression: %s.", find)
|
||||
compiled = regex.compile(find, flags=regex.V1)
|
||||
except Exception:
|
||||
logger.error("Compilation of regular expression %f failed.", find)
|
||||
continue
|
||||
# pylint: enable=W0703
|
||||
|
||||
entry = {
|
||||
'description': word,
|
||||
'find': find,
|
||||
'replace': replace,
|
||||
'regex': compiled
|
||||
}
|
||||
|
||||
result.append(entry)
|
||||
|
||||
logger.debug("Compiled %d regular expression(s).", len(result))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def load_text_from_file(src):
|
||||
"""Load content from a file."""
|
||||
logger = logging.getLogger('retf')
|
||||
logger.debug("Loading text from file %s.", src)
|
||||
with open(src, 'rb') as fpointer:
|
||||
text = fpointer.read()
|
||||
|
||||
return text
|
||||
|
||||
|
||||
def write_text_to_file(dest, text, no_backup, in_place):
|
||||
"""Write content into a file."""
|
||||
logger = logging.getLogger('retf')
|
||||
|
||||
if not no_backup:
|
||||
logger.debug("Copying %s to backup file %s.orig.", dest, dest)
|
||||
shutil.copy2(dest, "%s.orig" % dest)
|
||||
|
||||
if not in_place:
|
||||
dest = "%s.retf" % dest
|
||||
|
||||
logger.debug("Writing text to file %s.", dest)
|
||||
with open(dest, 'wb') as fpointer:
|
||||
fpointer.write(text)
|
||||
|
||||
|
||||
def initialize_logging(debug, less_verbose):
|
||||
"""Initialize the Logger."""
|
||||
logger = logging.getLogger(name='retf')
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s')
|
||||
handler = logging.StreamHandler()
|
||||
handler.setFormatter(formatter)
|
||||
logger.addHandler(handler)
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
if less_verbose:
|
||||
logger.setLevel(logging.WARN)
|
||||
|
||||
if debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
return logging.getLogger('retf')
|
||||
|
||||
|
||||
def parse_command_line_arguments():
|
||||
"""Parse the command line arguments."""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--debug", help="Print debugging messages.",
|
||||
action="store_true", default=False)
|
||||
parser.add_argument("--download", help="Download the latest RETF listing.",
|
||||
action="store_true", default=False)
|
||||
parser.add_argument("--less-verbose", help="Be less verbose.",
|
||||
action="store_true", default=False)
|
||||
parser.add_argument("--no-backup", help="Don't backup files.",
|
||||
action="store_true", default=False)
|
||||
parser.add_argument("--in-place", help="Resolve found errors in place.",
|
||||
action="store_true", default=False)
|
||||
parser.add_argument("--write-changes", action="store_true", default=False,
|
||||
help="Write resolved findings back to files.")
|
||||
parser.add_argument("--disabled", type=str, default=None,
|
||||
help="File containing the disabled rules.")
|
||||
parser.add_argument("--listing", help="File containing the RETF listing.",
|
||||
type=str, default=os.path.join(
|
||||
os.path.dirname(os.path.realpath(__file__)),
|
||||
'retf.lst'))
|
||||
parser.add_argument("--path", type=str, nargs='*', default=[],
|
||||
help="Path(s) that should be checked.")
|
||||
parser.add_argument("--extension", type=str, nargs='*', default=[],
|
||||
help="Only check files with specified extension(s).")
|
||||
parser.add_argument("--file", nargs='*', type=str, default=[],
|
||||
help="File(s) to check for typographical errors.")
|
||||
return (parser, parser.parse_args())
|
||||
|
||||
|
||||
def load_disabled_rules(src):
|
||||
"""Load disabled rules from YAML file."""
|
||||
logger = logging.getLogger('retf')
|
||||
listing = []
|
||||
|
||||
if src:
|
||||
try:
|
||||
listing = yaml.safe_load(open(src))
|
||||
for rule in listing:
|
||||
logger.debug("Rule '%s' is disabled.", rule)
|
||||
|
||||
except IOError:
|
||||
logger.error("loading disabled rules from file %s failed", src)
|
||||
|
||||
return listing
|
||||
|
||||
|
||||
def get_file_listing(paths, files, extensions):
|
||||
"""Generate listing with all files that should be check."""
|
||||
result = []
|
||||
if files:
|
||||
result += files
|
||||
|
||||
# pylint: disable=E1101
|
||||
for path in paths:
|
||||
if extensions:
|
||||
for extension in extensions:
|
||||
result += glob2.glob("%s/**/*.%s" % (path, extension))
|
||||
else:
|
||||
result += glob2.glob("%s/**/*" % path)
|
||||
# pylint: enable=E1101
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def check_file(src, rules, disabled):
|
||||
"""Applies a set of rules on a file."""
|
||||
logger = logging.getLogger('retf')
|
||||
logger.info("Checking file %s for typographical errors.", src)
|
||||
content = load_text_from_file(src)
|
||||
findings = 0
|
||||
|
||||
for rule in rules:
|
||||
if rule.get('description') in disabled:
|
||||
continue
|
||||
|
||||
logger.debug("%s: checking rule '%s'.", src,
|
||||
rule.get('description'))
|
||||
logger.debug(rule.get('find'))
|
||||
newcontent, count = rule.get('regex').subn(
|
||||
rule.get('replace'), content
|
||||
)
|
||||
|
||||
if count > 0:
|
||||
logger.warning("%d match(s) in file %s : %s.", count, src,
|
||||
rule.get('description'))
|
||||
findings += count
|
||||
content = newcontent
|
||||
|
||||
return (findings, content)
|
||||
|
||||
|
||||
def main():
|
||||
"""Entry point for this script."""
|
||||
|
||||
parser, args = parse_command_line_arguments()
|
||||
logger = initialize_logging(args.debug, args.less_verbose)
|
||||
|
||||
result = 0
|
||||
|
||||
if args.download:
|
||||
try:
|
||||
download_listing(args.listing)
|
||||
except DownloadRetfListingFailed as ex:
|
||||
logger.error("Downloading latest RETF listing failed: %s.", ex)
|
||||
result = 1
|
||||
|
||||
if not args.path and not args.file and not args.download:
|
||||
parser.print_help()
|
||||
result = 2
|
||||
|
||||
if not result and not os.path.isfile(args.listing):
|
||||
logger.error("RETF listing not found at %s.", args.listing)
|
||||
logger.info("Please download the RETF listing first by using the "
|
||||
"parameter --download.")
|
||||
result = 1
|
||||
|
||||
if not result:
|
||||
files = get_file_listing(args.path, args.file, args.extension)
|
||||
|
||||
rules = generate_listing(args.listing)
|
||||
disabled = load_disabled_rules(args.disabled)
|
||||
|
||||
all_findings = 0
|
||||
for check in files:
|
||||
if not os.path.isfile(check):
|
||||
continue
|
||||
|
||||
(findings, content) = check_file(check, rules, disabled)
|
||||
|
||||
if findings > 0:
|
||||
all_findings += findings
|
||||
logger.warning("%s finding(s) in file %s.", findings, check)
|
||||
|
||||
if findings > 0 and args.write_changes:
|
||||
write_text_to_file(check, content, args.no_backup,
|
||||
args.in_place)
|
||||
|
||||
if all_findings > 0:
|
||||
logger.warning("%s finding(s) in all checked files.", all_findings)
|
||||
result = 1
|
||||
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,21 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
repo_name = openstack-doc-tools
|
||||
api_site=True
|
||||
|
||||
# From api-ref/src/wadls/object-api/src/
|
||||
file_exception=os-object-api-1.0.wadl
|
||||
|
||||
ignore_dir=incubation
|
||||
ignore_dir=openstack-compute-api-1.0
|
||||
|
||||
# These two (or three) options need to come as pairs/triplets.
|
||||
# Either add publish_pair for each book/target_dir pair or not at all.
|
||||
# If publish_dir is not specified, book is used as publish_dir.
|
||||
book = api-quick-start
|
||||
target_dir = target/docbkx/webhelp/api-quick-start-onepager-external
|
||||
#publish_dir = api-quick-start
|
||||
|
||||
book = api-ref
|
||||
target_dir = target/docbkx/html
|
||||
#publish_dir = api-ref
|
@ -1 +0,0 @@
|
||||
.. include:: ../../autogenerate_config_docs/README.rst
|
@ -1,94 +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
|
||||
|
||||
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',
|
||||
'openstackdocstheme'
|
||||
]
|
||||
|
||||
# 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.
|
||||
repository_name = 'openstack/openstack-doc-tools'
|
||||
bug_tag = u'openstack-doc-tools'
|
||||
project = u'OpenStack-doc-tools'
|
||||
copyright = u'2017, 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'
|
||||
|
||||
# -- Options for HTML output --------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = [openstackdocstheme.get_html_theme_path()]
|
||||
|
||||
# html_static_path = ['static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# So that we can enable "log-a-bug" links from each output HTML page, this
|
||||
# variable must be set to a format that includes year, month, day, hours and
|
||||
# minutes.
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# 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}
|
||||
|
||||
# -- Options for manual page output -------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = []
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
@ -1 +0,0 @@
|
||||
.. include:: ../../README.rst
|
@ -1,21 +0,0 @@
|
||||
==============================================
|
||||
Welcome to openstack-doc-tool's documentation!
|
||||
==============================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
doc-tools-readme
|
||||
installation
|
||||
usage
|
||||
autogenerate_config_docs
|
||||
man/openstack-doc-test
|
||||
sitemap-readme
|
||||
release_notes
|
||||
|
||||
Search
|
||||
~~~~~~
|
||||
|
||||
* :ref:`search`
|
@ -1,16 +0,0 @@
|
||||
============
|
||||
Installation
|
||||
============
|
||||
|
||||
At the command line:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ pip install openstack-doc-tools
|
||||
|
||||
Or, if you have virtualenvwrapper installed:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ mkvirtualenv openstack-doc-tools
|
||||
$ pip install openstack-doc-tools
|
@ -1,117 +0,0 @@
|
||||
==================
|
||||
openstack-doc-test
|
||||
==================
|
||||
|
||||
------------------------------------------------------
|
||||
OpenStack Validation tool
|
||||
------------------------------------------------------
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
openstack-doc-test [options]
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
openstack-doc-test allows to test the validity of the OpenStack
|
||||
documentation content.
|
||||
|
||||
OPTIONS
|
||||
=======
|
||||
|
||||
**General options**
|
||||
|
||||
**--api-site**
|
||||
Special handling for api-site and other API repositories
|
||||
to handle WADL.
|
||||
|
||||
**--build-file-exception BUILD_FILE_EXCEPTION**
|
||||
File that will be skipped during delete and build checks to
|
||||
generate dependencies. This should be done for invalid XML files
|
||||
only.
|
||||
|
||||
**--check-build**
|
||||
Try to build books using modified files.
|
||||
|
||||
**--check-deletions**
|
||||
Check that deleted files are not used.
|
||||
|
||||
**--check-links**
|
||||
Check that linked URLs are valid and reachable.
|
||||
|
||||
**--check-niceness**
|
||||
Check the niceness of files, for example whitespace.
|
||||
|
||||
**--check-syntax**
|
||||
Check the syntax of modified files.
|
||||
|
||||
**--check-all**
|
||||
Run all checks (default if no arguments are given).
|
||||
|
||||
**--config-file PATH**
|
||||
Path to a config file to use. Multiple config files can be
|
||||
specified, with values in later files taking precedence.
|
||||
|
||||
**--debug**
|
||||
Enable debug code.
|
||||
|
||||
**--file-exception FILE_EXCEPTION**
|
||||
File that will be skipped during niceness and syntax validation.
|
||||
|
||||
**--force**
|
||||
Force the validation of all files and build all books.
|
||||
|
||||
**-h, --help**
|
||||
Show help message and exit.
|
||||
|
||||
**--ignore-dir IGNORE_DIR**
|
||||
Directory to ignore for building of manuals. The parameter can
|
||||
be passed multiple times to add several directories.
|
||||
|
||||
**--language LANGUAGE, -l LANGUAGE**
|
||||
Build translated manual for language in path generate/$LANGUAGE .
|
||||
|
||||
**--only-book ONLY_BOOK**
|
||||
Build each specified manual.
|
||||
|
||||
**--parallel**
|
||||
Build books in parallel (default).
|
||||
|
||||
**--print-unused-files**
|
||||
Print list of files that are not included anywhere as part of
|
||||
check-build.
|
||||
|
||||
**--publish**
|
||||
Setup content in publish-docs directory for publishing to
|
||||
external website.
|
||||
|
||||
**--verbose**
|
||||
Verbose execution.
|
||||
|
||||
**--version**
|
||||
Output version number.
|
||||
|
||||
FILES
|
||||
=====
|
||||
|
||||
Reads the file `doc-test.conf` in the top-level directory of the git
|
||||
repository for option processing.
|
||||
|
||||
Building of books will generate in the top-level directory of the git
|
||||
repository:
|
||||
|
||||
* a directory `publish-docs` with a copy of the build results.
|
||||
* for each book build a log file named `build-${book}.log.gz`.
|
||||
|
||||
SEE ALSO
|
||||
========
|
||||
|
||||
* `OpenStack Documentation <http://wiki.openstack.org/wiki/Documentation>`__
|
||||
|
||||
Bugs
|
||||
====
|
||||
|
||||
* openstack-doc-tools is hosted on Launchpad so you can view current
|
||||
bugs at
|
||||
`Bugs : openstack-doc-tools <https://bugs.launchpad.net/openstack-doc-tools/>`__
|
@ -1 +0,0 @@
|
||||
.. include:: ../../RELEASE_NOTES.rst
|
@ -1 +0,0 @@
|
||||
.. include:: ../../sitemap/README.rst
|
@ -1,9 +0,0 @@
|
||||
=====
|
||||
Usage
|
||||
=====
|
||||
|
||||
To use openstack-doc-tools in a project:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
import os_doc_tools
|
@ -1,8 +0,0 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The list of modules to copy from oslo-incubator.git
|
||||
module=log
|
||||
|
||||
# The base module to hold the copy of openstack.common
|
||||
base=os_doc_tools
|
||||
|
@ -1,18 +0,0 @@
|
||||
# Copyright 2012 OpenStack Foundation
|
||||
#
|
||||
# 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 pbr.version
|
||||
|
||||
|
||||
__version__ = pbr.version.VersionInfo('openstack-doc-tools').version_string()
|
@ -1,772 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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 subprocess
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
import os_doc_tools
|
||||
|
||||
DEVNULL = open(os.devnull, 'wb')
|
||||
MAXLINELENGTH = 78
|
||||
|
||||
|
||||
def use_help_flag(os_command):
|
||||
"""Use --help flag (instead of help keyword)
|
||||
|
||||
Returns true if the command requires a --help flag instead
|
||||
of a help keyword.
|
||||
"""
|
||||
|
||||
return os_command == "swift" or "-manage" in os_command
|
||||
|
||||
|
||||
def quote_rst(line):
|
||||
"""Convert special characters for RST output."""
|
||||
|
||||
line = line.replace('\\', '\\\\').replace('`', '\\`').replace('*', '\\*')
|
||||
|
||||
if 'DEPRECATED!' in line:
|
||||
line = line.replace('DEPRECATED!', '**DEPRECATED!**')
|
||||
elif 'DEPRECATED' in line:
|
||||
line = line.replace('DEPRECATED', '**DEPRECATED**')
|
||||
|
||||
if 'env[' in line:
|
||||
line = line.replace('env[', '``env[').replace(']', ']``')
|
||||
# work around for "Default=env[...]" at cinder
|
||||
line = line.replace('=``', '= ``')
|
||||
|
||||
return line
|
||||
|
||||
|
||||
def generate_heading(os_command, api_name, title,
|
||||
output_dir, os_filename, continue_on_error):
|
||||
"""Write RST file header.
|
||||
|
||||
:param os_command: client command to document
|
||||
:param api_name: string description of the API of os_command
|
||||
:param output_dir: directory to write output file to
|
||||
:param os_filename: name to create current output file as
|
||||
:param continue_on_error: continue even if there's an error
|
||||
"""
|
||||
|
||||
try:
|
||||
version = subprocess.check_output([os_command, "--version"],
|
||||
universal_newlines=True,
|
||||
stderr=subprocess.STDOUT)
|
||||
except OSError as e:
|
||||
if e.errno == os.errno.ENOENT:
|
||||
action = 'skipping' if continue_on_error else 'aborting'
|
||||
print("Command %s not found, %s." % (os_command, action))
|
||||
if continue_on_error:
|
||||
return
|
||||
else:
|
||||
sys.exit(1)
|
||||
# Extract version from "swift 0.3"
|
||||
version = version.splitlines()[-1].strip().rpartition(' ')[2]
|
||||
|
||||
print("Documenting '%s help (version %s)'" % (os_command, version))
|
||||
|
||||
os_file = open(os.path.join(output_dir, os_filename), 'w')
|
||||
os_file.write(".. ###################################################\n")
|
||||
os_file.write(".. ## WARNING ######################################\n")
|
||||
os_file.write(".. ############## WARNING ##########################\n")
|
||||
os_file.write(".. ########################## WARNING ##############\n")
|
||||
os_file.write(".. ###################################### WARNING ##\n")
|
||||
os_file.write(".. ###################################################\n")
|
||||
os_file.write(".. ###################################################\n")
|
||||
os_file.write(".. ##\n")
|
||||
os_file.write(".. This file is tool-generated. Do not edit manually.\n")
|
||||
os_file.write(".. http://docs.openstack.org/contributor-guide/\n")
|
||||
os_file.write(".. doc-tools/cli-reference.html\n")
|
||||
os_file.write(".. ##\n")
|
||||
os_file.write(".. ## WARNING ######################################\n")
|
||||
os_file.write(".. ############## WARNING ##########################\n")
|
||||
os_file.write(".. ########################## WARNING ##############\n")
|
||||
os_file.write(".. ###################################### WARNING ##\n")
|
||||
os_file.write(".. ###################################################\n\n")
|
||||
format_heading(title, 1, os_file)
|
||||
|
||||
if os_command == "heat":
|
||||
os_file.write(".. warning::\n\n")
|
||||
os_file.write(" The " + os_command + " CLI is deprecated\n")
|
||||
os_file.write(" in favor of python-openstackclient.\n\n")
|
||||
|
||||
os_file.write("The " + os_command + " client is the command-line ")
|
||||
os_file.write("interface (CLI) for the\n")
|
||||
os_file.write(api_name + "\n")
|
||||
os_file.write("and its extensions.\n\n")
|
||||
|
||||
os_file.write("This chapter documents :command:`" + os_command + "` ")
|
||||
os_file.write("version ``" + version + "``.\n\n")
|
||||
|
||||
os_file.write("For help on a specific :command:`" + os_command + "` ")
|
||||
os_file.write("command, enter:\n\n")
|
||||
|
||||
os_file.write(".. code-block:: console\n\n")
|
||||
if use_help_flag(os_command):
|
||||
os_file.write(" $ " + os_command + " COMMAND --help\n\n")
|
||||
else:
|
||||
os_file.write(" $ " + os_command + " help COMMAND\n\n")
|
||||
|
||||
os_file.write(".. _" + os_command + "_command_usage:\n\n")
|
||||
format_heading(os_command + " usage", 2, os_file)
|
||||
return os_file
|
||||
|
||||
|
||||
def is_option(string):
|
||||
"""Returns True if string specifies an argument."""
|
||||
|
||||
for x in string:
|
||||
if not (x.isupper() or x == '_' or x == ','):
|
||||
return False
|
||||
|
||||
if string.startswith('DEPRECATED'):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def extract_options(line):
|
||||
"""Extract command or option from line."""
|
||||
|
||||
# We have a command or parameter to handle
|
||||
# Differentiate:
|
||||
# 1. --version
|
||||
# 2. --timeout <seconds>
|
||||
# 3. --service <service>, --service-id <service>
|
||||
# 4. -v, --verbose
|
||||
# 5. -p PORT, --port PORT
|
||||
# 6. <backup> ID of the backup to restore.
|
||||
# 7. --alarm-action <Webhook URL>
|
||||
# 8. <NAME or ID> Name or ID of stack to resume.
|
||||
# 9. --json JSON JSON representation of node group template.
|
||||
# 10. --id <cluster_id> ID of the cluster to show.
|
||||
# 11. --instance "<opt=value,opt=value,...>"
|
||||
|
||||
split_line = line.split(None, 2)
|
||||
|
||||
if split_line[0].startswith("-"):
|
||||
last_was_option = True
|
||||
else:
|
||||
last_was_option = False
|
||||
|
||||
if (len(split_line) > 1 and
|
||||
('<' in split_line[0] or
|
||||
'<' in split_line[1] or
|
||||
'--' in split_line[1] or
|
||||
split_line[1].startswith(("-", '<', '{', '[')) or
|
||||
is_option(split_line[1]))):
|
||||
|
||||
words = line.split(None)
|
||||
|
||||
i = 0
|
||||
while i < len(words) - 1:
|
||||
if (('<' in words[i] and
|
||||
'>' not in words[i]) or
|
||||
('[' in words[i] and
|
||||
']' not in words[i])):
|
||||
words[i] += ' ' + words[i + 1]
|
||||
del words[i + 1]
|
||||
else:
|
||||
i += 1
|
||||
|
||||
skip_is_option = False
|
||||
while len(words) > 1:
|
||||
if words[1].startswith('DEPRECATED'):
|
||||
break
|
||||
if last_was_option:
|
||||
if (words[1].startswith(("-", '<', '{', '[', '"')) or
|
||||
(is_option(words[1]) and skip_is_option is False)):
|
||||
skip_is_option = False
|
||||
if words[1].isupper() or words[1].startswith('<'):
|
||||
skip_is_option = True
|
||||
words[0] = words[0] + ' ' + words[1]
|
||||
del words[1]
|
||||
else:
|
||||
break
|
||||
else:
|
||||
if words[1].startswith("-"):
|
||||
words[0] = words[0] + ' ' + words[1]
|
||||
del words[1]
|
||||
else:
|
||||
break
|
||||
|
||||
w0 = words[0]
|
||||
del words[0]
|
||||
w1 = ''
|
||||
if words:
|
||||
w1 = words[0]
|
||||
del words[0]
|
||||
for w in words:
|
||||
w1 += " " + w
|
||||
|
||||
if not w1:
|
||||
split_line = [w0]
|
||||
else:
|
||||
split_line = [w0, w1]
|
||||
else:
|
||||
split_line = line.split(None, 1)
|
||||
|
||||
return split_line
|
||||
|
||||
|
||||
def format_heading(heading, level, os_file):
|
||||
"""Nicely print heading.
|
||||
|
||||
:param heading: heading strings
|
||||
:param level: heading level
|
||||
:param os_file: open filehandle for output of RST file
|
||||
"""
|
||||
|
||||
if level == 1:
|
||||
os_file.write("=" * len(heading) + "\n")
|
||||
|
||||
os_file.write(heading + "\n")
|
||||
|
||||
if level == 1:
|
||||
os_file.write("=" * len(heading) + "\n\n")
|
||||
elif level == 2:
|
||||
os_file.write("~" * len(heading) + "\n\n")
|
||||
elif level == 3:
|
||||
os_file.write("-" * len(heading) + "\n\n")
|
||||
else:
|
||||
os_file.write("\n")
|
||||
|
||||
return
|
||||
|
||||
|
||||
def format_help(title, lines, os_file):
|
||||
"""Nicely print section of lines.
|
||||
|
||||
:param title: help title, if exist
|
||||
:param lines: strings to format
|
||||
:param os_file: open filehandle for output of RST file
|
||||
"""
|
||||
|
||||
close_entry = False
|
||||
if title:
|
||||
os_file.write("**" + title + ":**" + "\n\n")
|
||||
|
||||
continued_line = ''
|
||||
for line in lines:
|
||||
if not line or line[0] != ' ':
|
||||
break
|
||||
# We have to handle these cases:
|
||||
# 1. command Explanation
|
||||
# 2. command
|
||||
# Explanation on next line
|
||||
# 3. command Explanation continued
|
||||
# on next line
|
||||
# If there are more than 8 spaces, let's treat it as
|
||||
# explanation.
|
||||
if line.startswith(' '):
|
||||
# Explanation
|
||||
xline = continued_line + quote_rst(line.lstrip(' '))
|
||||
continued_line = ''
|
||||
# Concatenate the command options with "-"
|
||||
# For example:
|
||||
# see 'glance image-
|
||||
# show'
|
||||
if xline.endswith('-'):
|
||||
continued_line = xline
|
||||
continue
|
||||
# check niceness
|
||||
if len(xline) > (MAXLINELENGTH - 2):
|
||||
xline = xline.replace(' ', '\n ')
|
||||
os_file.write(" " + xline + "\n")
|
||||
continue
|
||||
# Now we have a command or parameter to handle
|
||||
split_line = extract_options(line)
|
||||
|
||||
if not close_entry:
|
||||
close_entry = True
|
||||
else:
|
||||
os_file.write("\n")
|
||||
|
||||
xline = split_line[0]
|
||||
|
||||
# check niceness work around for long option name, glance
|
||||
xline = xline.replace('[<RESOURCE_TYPE_ASSOCIATIONS> ...]',
|
||||
'[...]')
|
||||
|
||||
os_file.write("``" + xline + "``\n")
|
||||
|
||||
if len(split_line) > 1:
|
||||
# Explanation
|
||||
xline = continued_line + quote_rst(split_line[1])
|
||||
continued_line = ''
|
||||
# Concatenate the command options with "-"
|
||||
# For example:
|
||||
# see 'glance image-
|
||||
# show'
|
||||
if xline.endswith('-'):
|
||||
continued_line = xline
|
||||
continue
|
||||
# check niceness
|
||||
if len(xline) > (MAXLINELENGTH - 2):
|
||||
# check niceness
|
||||
xline = xline.replace(' ', '\n ')
|
||||
os_file.write(" " + xline + "\n")
|
||||
|
||||
os_file.write("\n")
|
||||
|
||||
return
|
||||
|
||||
|
||||
def generate_command(os_command, os_file):
|
||||
"""Convert os_command --help to RST.
|
||||
|
||||
:param os_command: client command to document
|
||||
:param os_file: open filehandle for output of RST file
|
||||
"""
|
||||
|
||||
if use_help_flag(os_command):
|
||||
help_lines = subprocess.check_output([os_command, "--help"],
|
||||
universal_newlines=True,
|
||||
stderr=DEVNULL).split('\n')
|
||||
else:
|
||||
help_lines = subprocess.check_output([os_command, "help"],
|
||||
universal_newlines=True,
|
||||
stderr=DEVNULL).split('\n')
|
||||
|
||||
ignore_next_lines = False
|
||||
next_line_screen = True
|
||||
line_index = -1
|
||||
in_screen = False
|
||||
subcommands = 'complete'
|
||||
for line in help_lines:
|
||||
line_index += 1
|
||||
if line and line[0] != ' ':
|
||||
# XXX: Might have whitespace before!!
|
||||
if '<subcommands>' in line:
|
||||
ignore_next_lines = False
|
||||
continue
|
||||
if 'Positional arguments' in line:
|
||||
ignore_next_lines = True
|
||||
next_line_screen = True
|
||||
os_file.write("\n\n")
|
||||
in_screen = False
|
||||
if os_command != "glance":
|
||||
format_help('Subcommands',
|
||||
help_lines[line_index + 2:], os_file)
|
||||
continue
|
||||
if line.startswith(('Optional arguments:', 'Optional:',
|
||||
'Options:', 'optional arguments')):
|
||||
if in_screen:
|
||||
os_file.write("\n\n")
|
||||
in_screen = False
|
||||
os_file.write(".. _" + os_command + "_command_options:\n\n")
|
||||
format_heading(os_command + " optional arguments", 2, os_file)
|
||||
format_help('', help_lines[line_index + 1:], os_file)
|
||||
next_line_screen = True
|
||||
ignore_next_lines = True
|
||||
continue
|
||||
# magnum
|
||||
if line.startswith('Common auth options'):
|
||||
if in_screen:
|
||||
os_file.write("\n\n")
|
||||
in_screen = False
|
||||
os_file.write("\n")
|
||||
os_file.write(os_command)
|
||||
os_file.write(".. _" + os_command + "_common_auth:\n\n")
|
||||
format_heading(os_command + " common authentication arguments",
|
||||
2, os_file)
|
||||
format_help('', help_lines[line_index + 1:], os_file)
|
||||
next_line_screen = True
|
||||
ignore_next_lines = True
|
||||
continue
|
||||
# neutron
|
||||
if line.startswith('Commands for API v2.0:'):
|
||||
if in_screen:
|
||||
os_file.write("\n\n")
|
||||
in_screen = False
|
||||
os_file.write(".. _" + os_command + "_common_api_v2:\n\n")
|
||||
format_heading(os_command + " API v2.0 commands", 2, os_file)
|
||||
format_help('', help_lines[line_index + 1:], os_file)
|
||||
next_line_screen = True
|
||||
ignore_next_lines = True
|
||||
continue
|
||||
# swift
|
||||
if line.startswith('Examples:'):
|
||||
os_file.write(".. _" + os_command + "_examples:\n\n")
|
||||
format_heading(os_command + " examples", 2, os_file)
|
||||
next_line_screen = True
|
||||
ignore_next_lines = False
|
||||
continue
|
||||
# all
|
||||
if not line.startswith('usage'):
|
||||
continue
|
||||
if not ignore_next_lines:
|
||||
if next_line_screen:
|
||||
os_file.write(".. code-block:: console\n\n")
|
||||
os_file.write(" " + line)
|
||||
next_line_screen = False
|
||||
in_screen = True
|
||||
elif line:
|
||||
os_file.write("\n " + line.rstrip())
|
||||
# subcommands (select bash-completion, complete for bash-completion)
|
||||
if 'bash-completion' in line:
|
||||
subcommands = 'bash-completion'
|
||||
|
||||
if in_screen:
|
||||
os_file.write("\n\n")
|
||||
|
||||
return subcommands
|
||||
|
||||
|
||||
def generate_subcommand(os_command, os_subcommand, os_file, extra_params,
|
||||
suffix, title_suffix):
|
||||
"""Convert os_command help os_subcommand to RST.
|
||||
|
||||
:param os_command: client command to document
|
||||
:param os_subcommand: client subcommand to document
|
||||
:param os_file: open filehandle for output of RST file
|
||||
:param extra_params: Extra parameter to pass to os_command
|
||||
:param suffix: Extra suffix to add to link ID
|
||||
:param title_suffix: Extra suffix for title
|
||||
"""
|
||||
|
||||
print("Documenting subcommand '%s'..." % os_subcommand)
|
||||
|
||||
args = [os_command]
|
||||
if extra_params:
|
||||
args.extend(extra_params)
|
||||
if use_help_flag(os_command):
|
||||
args.append(os_subcommand)
|
||||
args.append("--help")
|
||||
else:
|
||||
args.append("help")
|
||||
args.append(os_subcommand)
|
||||
help_lines = subprocess.check_output(args,
|
||||
universal_newlines=True,
|
||||
stderr=DEVNULL)
|
||||
|
||||
help_lines_lower = help_lines.lower()
|
||||
if 'positional arguments' in help_lines_lower:
|
||||
index = help_lines_lower.index('positional arguments')
|
||||
elif 'optional arguments' in help_lines_lower:
|
||||
index = help_lines_lower.index('optional arguments')
|
||||
else:
|
||||
index = len(help_lines_lower)
|
||||
|
||||
if 'deprecated' in (help_lines_lower[0:index]):
|
||||
print("Subcommand '%s' is deprecated, skipping." % os_subcommand)
|
||||
return
|
||||
|
||||
help_lines = help_lines.split('\n')
|
||||
|
||||
os_subcommandid = os_subcommand.replace(' ', '_')
|
||||
os_file.write(".. _" + os_command + "_" + os_subcommandid + suffix)
|
||||
os_file.write(":\n\n")
|
||||
format_heading(os_command + " " + os_subcommand + title_suffix, 3, os_file)
|
||||
|
||||
if os_command == "swift":
|
||||
next_line_screen = False
|
||||
os_file.write(".. code-block:: console\n\n")
|
||||
os_file.write("Usage: swift " + os_subcommand + "\n\n")
|
||||
in_para = True
|
||||
else:
|
||||
next_line_screen = True
|
||||
in_para = False
|
||||
if extra_params:
|
||||
extra_paramstr = ' '.join(extra_params)
|
||||
help_lines[0] = help_lines[0].replace(os_command, "%s %s" %
|
||||
(os_command, extra_paramstr))
|
||||
line_index = -1
|
||||
# Content is:
|
||||
# usage...
|
||||
#
|
||||
# Description
|
||||
#
|
||||
# Arguments
|
||||
|
||||
skip_lines = False
|
||||
for line in help_lines:
|
||||
line_index += 1
|
||||
if line.startswith('Usage:') and os_command == "swift":
|
||||
line = line[len("Usage: "):]
|
||||
if line.startswith(('Arguments:',
|
||||
'Positional arguments:', 'positional arguments',
|
||||
'Optional arguments', 'optional arguments',
|
||||
'Required arguments', 'required arguments')):
|
||||
if in_para:
|
||||
in_para = False
|
||||
os_file.write("\n")
|
||||
if line.startswith(('Positional arguments',
|
||||
'positional arguments')):
|
||||
format_help('Positional arguments',
|
||||
help_lines[line_index + 1:], os_file)
|
||||
skip_lines = True
|
||||
continue
|
||||
elif line.startswith(('Optional arguments:',
|
||||
'optional arguments')):
|
||||
format_help('Optional arguments',
|
||||
help_lines[line_index + 1:], os_file)
|
||||
skip_lines = True
|
||||
continue
|
||||
elif line.startswith(('Required arguments:',
|
||||
'required arguments')):
|
||||
format_help('Required arguments',
|
||||
help_lines[line_index + 1:], os_file)
|
||||
skip_lines = True
|
||||
continue
|
||||
else:
|
||||
format_help('Arguments', help_lines[line_index + 1:], os_file)
|
||||
break
|
||||
if skip_lines:
|
||||
continue
|
||||
if not line:
|
||||
if not in_para:
|
||||
os_file.write("\n")
|
||||
in_para = True
|
||||
continue
|
||||
if next_line_screen:
|
||||
os_file.write(".. code-block:: console\n\n")
|
||||
os_file.write(" " + line + "\n")
|
||||
next_line_screen = False
|
||||
elif line.startswith(' '):
|
||||
# ceilometer alarm-gnocchi-aggregation-by-metrics-threshold-create
|
||||
# has 7 white space indentation
|
||||
if not line.isspace():
|
||||
# skip blank line, such as "trove help cluster-grow" command.
|
||||
os_file.write(" " + line + "\n")
|
||||
else:
|
||||
xline = quote_rst(line)
|
||||
if (len(xline) > MAXLINELENGTH):
|
||||
# check niceness
|
||||
xline = xline.replace(' ', '\n')
|
||||
os_file.write(xline + "\n")
|
||||
|
||||
if in_para:
|
||||
os_file.write("\n")
|
||||
|
||||
|
||||
def discover_subcommands(os_command, subcommands, extra_params):
|
||||
"""Discover all help subcommands for the given command"
|
||||
|
||||
:param os_command: client command whose subcommands need to be discovered
|
||||
:param subcommands: list or type ('complete' or 'bash-completion')
|
||||
of subcommands to document
|
||||
:param extra_params: Extra parameter to pass to os_command.
|
||||
:return: the list of subcommands discovered
|
||||
:rtype: list(str)
|
||||
"""
|
||||
if extra_params is None:
|
||||
extra_params = ''
|
||||
print(("Discovering subcommands of '%s' %s ..."
|
||||
% (os_command, extra_params)))
|
||||
blacklist = ['bash-completion', 'complete', 'help']
|
||||
if type(subcommands) is str:
|
||||
args = [os_command]
|
||||
if extra_params:
|
||||
args.extend(extra_params)
|
||||
if subcommands == 'complete':
|
||||
subcommands = []
|
||||
args.append('complete')
|
||||
lines = subprocess.check_output(
|
||||
args, universal_newlines=True, stderr=DEVNULL).split('\n')
|
||||
delim = ' '
|
||||
# if the cmds= line contains '-' then use that as a delim
|
||||
for line in lines:
|
||||
if '-' in line and 'cmds=' in line:
|
||||
delim = '-'
|
||||
break
|
||||
for line in [x.strip() for x in lines
|
||||
if x.strip().startswith('cmds_') and '-' in x]:
|
||||
subcommand, _ = line.split('=')
|
||||
subcommand = subcommand.replace(
|
||||
'cmds_', '').replace('_', delim)
|
||||
subcommands.append(subcommand)
|
||||
else:
|
||||
args.append('bash-completion')
|
||||
subcommands = subprocess.check_output(
|
||||
args,
|
||||
universal_newlines=True).strip().split('\n')[-1].split()
|
||||
|
||||
subcommands = sorted([o for o in subcommands if not (o.startswith('-') or
|
||||
o in blacklist)])
|
||||
|
||||
print("%d subcommands discovered." % len(subcommands))
|
||||
return subcommands
|
||||
|
||||
|
||||
def generate_subcommands(os_command, os_file, subcommands, extra_params,
|
||||
suffix, title_suffix):
|
||||
"""Convert os_command help subcommands for all subcommands to RST.
|
||||
|
||||
:param os_command: client command to document
|
||||
:param os_file: open filehandle for output of RST file
|
||||
:param subcommands: list or type ('complete' or 'bash-completion')
|
||||
of subcommands to document
|
||||
:param extra_params: Extra parameter to pass to os_command.
|
||||
:param suffix: Extra suffix to add to link ID
|
||||
:param title_suffix: Extra suffix for title
|
||||
"""
|
||||
for subcommand in subcommands:
|
||||
generate_subcommand(os_command, subcommand, os_file, extra_params,
|
||||
suffix, title_suffix)
|
||||
print("%d subcommands documented." % len(subcommands))
|
||||
|
||||
|
||||
def discover_and_generate_subcommands(os_command, os_file, subcommands,
|
||||
extra_params, suffix, title_suffix):
|
||||
"""Convert os_command help subcommands for all subcommands to RST.
|
||||
|
||||
:param os_command: client command to document
|
||||
:param os_file: open filehandle for output of RST file
|
||||
:param subcommands: list or type ('complete' or 'bash-completion')
|
||||
of subcommands to document
|
||||
:param extra_params: Extra parameter to pass to os_command.
|
||||
:param suffix: Extra suffix to add to link ID
|
||||
:param title_suffix: Extra suffix for title
|
||||
"""
|
||||
subcommands = discover_subcommands(os_command, subcommands, extra_params)
|
||||
generate_subcommands(os_command, os_file, subcommands, extra_params,
|
||||
suffix, title_suffix)
|
||||
|
||||
|
||||
def _get_clients_filename():
|
||||
return os.path.join(os.path.dirname(__file__),
|
||||
'resources/clients.yaml')
|
||||
|
||||
|
||||
def get_clients():
|
||||
"""Load client definitions from the resource file."""
|
||||
fname = _get_clients_filename()
|
||||
clients = yaml.load(open(fname, 'r'))
|
||||
return clients
|
||||
|
||||
|
||||
def document_single_project(os_command, output_dir, continue_on_error):
|
||||
"""Create documentation for os_command."""
|
||||
|
||||
clients = get_clients()
|
||||
|
||||
if os_command not in clients:
|
||||
print("'%s' command not yet handled" % os_command)
|
||||
print("(Command must be defined in '%s')" % _get_clients_filename())
|
||||
if continue_on_error:
|
||||
return False
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
print("Documenting '%s'" % os_command)
|
||||
|
||||
data = clients[os_command]
|
||||
if 'name' in data:
|
||||
api_name = "%s API" % data['name']
|
||||
title = "%s command-line client" % data.get('title', data['name'])
|
||||
else:
|
||||
api_name = ''
|
||||
title = data.get('title', '')
|
||||
|
||||
out_filename = os_command + ".rst"
|
||||
out_file = generate_heading(os_command, api_name, title,
|
||||
output_dir, out_filename,
|
||||
continue_on_error)
|
||||
if not out_file:
|
||||
if continue_on_error:
|
||||
return False
|
||||
else:
|
||||
sys.exit(-1)
|
||||
|
||||
subcommands = generate_command(os_command, out_file)
|
||||
if subcommands == 'complete' and data.get('subcommands'):
|
||||
subcommands = data.get('subcommands')
|
||||
|
||||
discover_and_generate_subcommands(os_command, out_file, subcommands,
|
||||
None, "", "")
|
||||
|
||||
print("Finished.\n")
|
||||
out_file.close()
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
clients = get_clients()
|
||||
api_clients = sorted([x for x in clients if not x.endswith('-manage')])
|
||||
manage_clients = sorted([x for x in clients if x.endswith('-manage')])
|
||||
all_clients = api_clients + manage_clients
|
||||
|
||||
parser = argparse.ArgumentParser(description="Generate RST files "
|
||||
"to document python-PROJECTclients.")
|
||||
parser.add_argument('clients', metavar='client', nargs='*',
|
||||
help="OpenStack command to document. Specify "
|
||||
"multiple times to generate documentation for "
|
||||
"multiple clients. One of: " +
|
||||
", ".join(all_clients) + ".")
|
||||
parser.add_argument("--all", help="Document all clients. "
|
||||
"Namely " + ", ".join(all_clients) + ".",
|
||||
action="store_true")
|
||||
parser.add_argument("--all-api", help="Document all API clients. "
|
||||
"Namely " + ", ".join(clients.keys()) + ".",
|
||||
action="store_true")
|
||||
parser.add_argument("--all-manage", help="Document all manage clients. "
|
||||
"Namely " + ", ".join(manage_clients) + ".",
|
||||
action="store_true")
|
||||
parser.add_argument("--output-dir", default=".",
|
||||
help="Directory to write generated files to")
|
||||
parser.add_argument("--continue-on-error", default=False,
|
||||
help="Continue with remaining clients even if an "
|
||||
"error occurs generating a client file.",
|
||||
action="store_true")
|
||||
parser.add_argument("--version", default=False,
|
||||
help="Show program's version number and exit.",
|
||||
action="store_true")
|
||||
prog_args = parser.parse_args()
|
||||
|
||||
client_list = []
|
||||
if prog_args.all or prog_args.all_api or prog_args.all_manage:
|
||||
if prog_args.all or prog_args.all_api:
|
||||
client_list = api_clients
|
||||
if prog_args.all or prog_args.all_manage:
|
||||
client_list.extend(manage_clients)
|
||||
elif prog_args.clients:
|
||||
client_list = prog_args.clients
|
||||
|
||||
if not prog_args or 'help' in [client.lower() for client in client_list]:
|
||||
parser.print_help()
|
||||
sys.exit(0)
|
||||
elif prog_args.version:
|
||||
print(os_doc_tools.__version__)
|
||||
sys.exit(0)
|
||||
|
||||
if not client_list:
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
print("OpenStack Auto Documenting of Commands (using "
|
||||
"openstack-doc-tools version %s)\n"
|
||||
% os_doc_tools.__version__)
|
||||
|
||||
success_list = []
|
||||
error_list = []
|
||||
for client in client_list:
|
||||
if document_single_project(
|
||||
client, prog_args.output_dir, prog_args.continue_on_error):
|
||||
success_list.append(client)
|
||||
else:
|
||||
error_list.append(client)
|
||||
|
||||
if success_list:
|
||||
print("Generated documentation for: %s" % ", ".join(success_list))
|
||||
if error_list:
|
||||
print("Generation failed for: %s" % ", ".join(error_list))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,92 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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 glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def get_pdf_link(root, publish_path):
|
||||
p = '%s/*.pdf' % root
|
||||
re = glob.glob(p)
|
||||
if len(re) == 0:
|
||||
return ''
|
||||
filename = os.path.basename(re[0])
|
||||
path = os.path.relpath(root, publish_path)
|
||||
return ' <a href="%s/%s">(pdf)</a>' % (path, filename)
|
||||
|
||||
|
||||
def generate_index_file(publish_path):
|
||||
"""Generate index.html file in publish_path."""
|
||||
|
||||
if not os.path.isdir(publish_path):
|
||||
os.mkdir(publish_path)
|
||||
|
||||
index_file = open(os.path.join(publish_path, 'index.html'), 'w')
|
||||
|
||||
index_file.write(
|
||||
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"\n'
|
||||
'"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
|
||||
'<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">\n'
|
||||
'<body>\n'
|
||||
'<h1>Generated documents</h1>\n')
|
||||
|
||||
links = {}
|
||||
for root, dirs, files in os.walk(publish_path):
|
||||
|
||||
dirs[:] = [d for d in dirs if d not in ['common', 'webapp', 'content',
|
||||
'www', 'samples']]
|
||||
|
||||
# Ignore top-level index.html files
|
||||
if root == publish_path:
|
||||
continue
|
||||
|
||||
pdf_link = get_pdf_link(root, publish_path)
|
||||
|
||||
if os.path.isfile(os.path.join(root, 'index.html')):
|
||||
path = os.path.relpath(root, publish_path)
|
||||
links[path] = ('<a href="%s/index.html">%s</a>%s\n' %
|
||||
(path, path.replace('draft/', ''), pdf_link))
|
||||
|
||||
for entry in sorted([s for s in links if not s.startswith('draft/')]):
|
||||
index_file.write(links[entry])
|
||||
index_file.write('<br/>\n')
|
||||
|
||||
drafts = [s for s in links if s.startswith('draft/')]
|
||||
if drafts:
|
||||
index_file.write('<h2>Draft guides</h2>\n')
|
||||
for entry in sorted(drafts):
|
||||
index_file.write(links[entry])
|
||||
index_file.write('<br/>\n')
|
||||
|
||||
if os.path.isfile(os.path.join(publish_path, 'www-index.html')):
|
||||
index_file.write('<h2>WWW index pages</h2>\n')
|
||||
index_file.write('<a href="www-index.html">List of generated '
|
||||
'WWW pages</a>\n')
|
||||
index_file.write('</body>\n'
|
||||
'</html>\n')
|
||||
index_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Generate index file.")
|
||||
parser.add_argument('directory', metavar='DIR',
|
||||
help="Directory to search.")
|
||||
args = parser.parse_args()
|
||||
|
||||
generate_index_file(args.directory)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,162 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# 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.
|
||||
|
||||
"""
|
||||
|
||||
Usage:
|
||||
jsoncheck.py [-f] FILES
|
||||
|
||||
Checks JSON syntax and optionally reformats (pretty-prints) the valid files.
|
||||
|
||||
Optional:
|
||||
- demjson Python library (better diagnostics for invalid JSON synax)
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import collections
|
||||
import json
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
try:
|
||||
import demjson
|
||||
except ImportError:
|
||||
demjson = None
|
||||
sys.stderr.write("Cannot import the demjson Python module. Diagnostics "
|
||||
"for invalid JSON files\nwill be limited.\n")
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Public interface
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def check_syntax(path):
|
||||
"""Check syntax of one JSON file."""
|
||||
_process_file(path)
|
||||
|
||||
|
||||
def check_formatting(path):
|
||||
"""Check formatting of one JSON file."""
|
||||
_process_file(path, formatting='check')
|
||||
|
||||
|
||||
def fix_formatting(path, verbose=False):
|
||||
"""Fix formatting of one JSON file."""
|
||||
_process_file(path, formatting='fix', verbose=verbose)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Implementation details
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _indent_note(note):
|
||||
"""Indents and wraps a string."""
|
||||
indented_note = []
|
||||
# Split into single lines in case the argument is pre-formatted.
|
||||
for line in note.splitlines():
|
||||
indented_note.append(textwrap.fill(line, initial_indent=4 * ' ',
|
||||
subsequent_indent=12 * ' ',
|
||||
width=80))
|
||||
return "\n".join(indented_note)
|
||||
|
||||
|
||||
def _get_demjson_diagnostics(raw):
|
||||
"""Get diagnostics string for invalid JSON files from demjson."""
|
||||
errstr = None
|
||||
try:
|
||||
demjson.decode(raw, strict=True)
|
||||
except demjson.JSONError as err:
|
||||
errstr = err.pretty_description()
|
||||
return errstr
|
||||
|
||||
|
||||
class ParserException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _parse_json(raw):
|
||||
"""Parse raw JSON file."""
|
||||
try:
|
||||
parsed = json.loads(raw, object_pairs_hook=collections.OrderedDict)
|
||||
except ValueError as err:
|
||||
note = str(err)
|
||||
# if demjson is available, print its diagnostic string as well
|
||||
if demjson:
|
||||
demerr = _get_demjson_diagnostics(raw)
|
||||
if demerr:
|
||||
note += "\n" + demerr
|
||||
raise ParserException(note)
|
||||
else:
|
||||
return parsed
|
||||
|
||||
|
||||
def _format_parsed_json(parsed):
|
||||
"""Pretty-print JSON file content while retaining key order."""
|
||||
return json.dumps(parsed, sort_keys=False, separators=(',', ': '),
|
||||
indent=4) + "\n"
|
||||
|
||||
|
||||
def _process_file(path, formatting=None, verbose=False):
|
||||
"""Check syntax/formatting and fix formatting of a JSON file.
|
||||
|
||||
:param formatting: one of 'check' or 'fix' (default: None)
|
||||
|
||||
Raises ValueError if JSON syntax is invalid or reformatting needed.
|
||||
"""
|
||||
with open(path, 'r') as infile:
|
||||
raw = infile.read()
|
||||
try:
|
||||
parsed = _parse_json(raw)
|
||||
except ParserException as err:
|
||||
raise ValueError(err)
|
||||
else:
|
||||
if formatting in ('check', 'fix'):
|
||||
formatted = _format_parsed_json(parsed)
|
||||
if formatted != raw:
|
||||
if formatting == "fix":
|
||||
with open(path, 'w') as outfile:
|
||||
outfile.write(formatted)
|
||||
if verbose:
|
||||
print("%s\n%s" % (path,
|
||||
_indent_note("Reformatted")))
|
||||
else:
|
||||
raise ValueError("Reformatting needed")
|
||||
elif formatting is not None:
|
||||
# for the benefit of external callers
|
||||
raise ValueError("Called with invalid formatting value.")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate and reformat JSON"
|
||||
"files.")
|
||||
parser.add_argument('files', metavar='FILES', nargs='+')
|
||||
parser.add_argument('-f', '--formatting', choices=['check', 'fix'],
|
||||
help='check or fix formatting of JSON files')
|
||||
args = parser.parse_args()
|
||||
|
||||
exit_status = 0
|
||||
for path in args.files:
|
||||
try:
|
||||
_process_file(path, args.formatting, verbose=True)
|
||||
except ValueError as err:
|
||||
print("%s\n%s" % (path, _indent_note(str(err))))
|
||||
exit_status = 1
|
||||
|
||||
return exit_status
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
@ -1,134 +0,0 @@
|
||||
---
|
||||
aodh:
|
||||
name: Telemetry Alarming service (aodh)
|
||||
subcommands:
|
||||
- alarm create
|
||||
- alarm delete
|
||||
- alarm list
|
||||
- alarm show
|
||||
- alarm state get
|
||||
- alarm state set
|
||||
- alarm update
|
||||
- alarm-history search
|
||||
- alarm-history show
|
||||
- capabilities list
|
||||
barbican:
|
||||
name: Key Manager service (barbican)
|
||||
subcommands: complete
|
||||
ceilometer:
|
||||
name: Telemetry Data Collection service (ceilometer)
|
||||
cinder:
|
||||
name: Block Storage service (cinder)
|
||||
cloudkitty:
|
||||
name: Rating service (cloudkitty)
|
||||
congress:
|
||||
name: Governance service (congress)
|
||||
designate:
|
||||
name: DNS service (designate)
|
||||
subcommands: complete
|
||||
freezer:
|
||||
name: Backup, Restore, and Disaster Recovery service (freezer)
|
||||
glance:
|
||||
name: Image service (glance)
|
||||
gnocchi:
|
||||
name: A time series storage and resources index service (gnocchi)
|
||||
subcommands:
|
||||
- archive-policy create
|
||||
- archive-policy delete
|
||||
- archive-policy list
|
||||
- archive-policy show
|
||||
- archive-policy update
|
||||
- archive-policy-rule create
|
||||
- archive-policy-rule delete
|
||||
- archive-policy-rule list
|
||||
- archive-policy-rule show
|
||||
- benchmark measures add
|
||||
- benchmark measures show
|
||||
- benchmark metric create
|
||||
- benchmark metric show
|
||||
- capabilities list
|
||||
- complete
|
||||
- help
|
||||
- measures add
|
||||
- measures aggregation
|
||||
- measures batch-metrics
|
||||
- measures batch-resources-metrics
|
||||
- measures show
|
||||
- metric create
|
||||
- metric delete
|
||||
- metric list
|
||||
- metric show
|
||||
- resource batch delete
|
||||
- resource create
|
||||
- resource delete
|
||||
- resource history
|
||||
- resource list
|
||||
- resource show
|
||||
- resource update
|
||||
- resource-type create
|
||||
- resource-type delete
|
||||
- resource-type list
|
||||
- resource-type show
|
||||
- resource-type update
|
||||
- status
|
||||
heat:
|
||||
name: Orchestration service (heat)
|
||||
ironic:
|
||||
name: Bare Metal service (ironic)
|
||||
kite:
|
||||
name: A service for managing and distributing message encryption keys (kite)
|
||||
magnum:
|
||||
name: Container Infrastructure Management service (magnum)
|
||||
manila:
|
||||
name: Shared File Systems service (manila)
|
||||
mistral:
|
||||
name: Workflow service (mistral)
|
||||
subcommands: complete
|
||||
monasca:
|
||||
name: Monitoring (monasca)
|
||||
murano:
|
||||
name: Application Catalog service (murano)
|
||||
neutron:
|
||||
name: Networking service (neutron)
|
||||
nova:
|
||||
name: Compute service (nova)
|
||||
senlin:
|
||||
name: Clustering service (senlin)
|
||||
solum:
|
||||
name: Software Development Lifecycle Automation service (solum)
|
||||
swift:
|
||||
name: Object Storage service (swift)
|
||||
subcommands:
|
||||
- auth
|
||||
- capabilities
|
||||
- delete
|
||||
- download
|
||||
- list
|
||||
- post
|
||||
- stat
|
||||
- tempurl
|
||||
- upload
|
||||
tacker:
|
||||
name: NFV Orchestration service (tacker)
|
||||
tripleo:
|
||||
name: Deployment service (tripleo)
|
||||
trove:
|
||||
name: Database service (trove)
|
||||
trove-manage:
|
||||
name: Database service management utility
|
||||
subcommands:
|
||||
- datastore_update
|
||||
- datastore_version_flavor_add
|
||||
- datastore_version_flavor_delete
|
||||
- datastore_version_update
|
||||
- db_downgrade
|
||||
- db_load_datastore_config_parameters
|
||||
- db_recreate
|
||||
- db_sync
|
||||
- db_upgrade
|
||||
vitrage:
|
||||
name: RCA (Root Cause Analysis) service (vitrage)
|
||||
watcher:
|
||||
name: Infrastructure Optimization service (watcher)
|
||||
zaqar:
|
||||
name: Message service (zaqar)
|
@ -1,17 +0,0 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
#
|
||||
# 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 pbr.version
|
||||
|
||||
version_info = pbr.version.VersionInfo('openstack-doc-tools')
|
31
pylintrc
31
pylintrc
@ -1,31 +0,0 @@
|
||||
# The format of this file isn't really documented; just use --generate-rcfile
|
||||
|
||||
[Messages Control]
|
||||
# C0111: Don't require docstrings on every method
|
||||
# W0511: TODOs in code comments are fine.
|
||||
# W0142: *args and **kwargs are fine.
|
||||
# W0622: Redefining id is fine.
|
||||
disable=C0111,W0511,W0142,W0622
|
||||
|
||||
[Basic]
|
||||
# Variable names can be 1 to 31 characters long, with lowercase and underscores
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
|
||||
# Argument names can be 2 to 31 characters long, with lowercase and underscores
|
||||
argument-rgx=[a-z_][a-z0-9_]{1,30}$
|
||||
|
||||
# Method names should be at least 3 characters long
|
||||
# and be lowercased with underscores
|
||||
method-rgx=([a-z_][a-z0-9_]{2,50}|setUp|tearDown)$
|
||||
|
||||
[Design]
|
||||
max-public-methods=100
|
||||
min-public-methods=0
|
||||
max-args=6
|
||||
|
||||
[Variables]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
# _ is used by our localization
|
||||
additional-builtins=_
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- Use reno for release note management.
|
@ -1,7 +0,0 @@
|
||||
other:
|
||||
- |
|
||||
For translated manuals, the command ``doc-tools-check-languages``
|
||||
now places a marker file in the root of each directory. This
|
||||
marker file is needed for proper publishing in the OpenStack CI
|
||||
environment. For details, see
|
||||
http://specs.openstack.org/openstack-infra/infra-specs/specs/doc-publishing.html.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- "``autohelp.py`` now allows overrides of sections, defined in ``<project>.overrides`` configuration files."
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Update CLI Reference generation tool for RST.
|
||||
To migrate CLI Reference from DocBook to RST,
|
||||
output the documentation in RST format,
|
||||
with a few work arounds for RST/Sphinx specific issues.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- The configuration items for autohelp are now in the openstack-manuals
|
||||
repository.
|
@ -1,6 +0,0 @@
|
||||
---
|
||||
prelude: >
|
||||
The most important change in this release is the removal
|
||||
of DocBook XML support. Support for checking, building, and
|
||||
translation of DocBook XML files has been removed. The tools now
|
||||
only handle RST files.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fix building of translations on older branches where the DebConf
|
||||
Install Guide does not exist.
|
@ -1,4 +0,0 @@
|
||||
features:
|
||||
- The documentation for this repo has been improved and is now
|
||||
published at
|
||||
https://docs.openstack.org/developer/openstack-doc-tools.
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Fix doc-tools-check-languages, it used a wrong way
|
||||
of passing arguments to tox and failed therefore with
|
||||
tox 2.5.0.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- Set the bug report project to openstack-i18n
|
||||
for the translated documents.
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The `extract_swift_flags` tool no longer supports the `docbook` command.
|
||||
This previously allowed outputting of Docbook-formatted text but the
|
||||
feature was broken and unsupported by the community. reStructuredText
|
||||
should be used for everything.
|
||||
- |
|
||||
The `extract_swift_flags` tool no longer supports the `--from` argument.
|
||||
All Swift documentation has been converted to reStructuredText meaning
|
||||
there is no reason to keep this around.
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
The `extract_swift_flags.py` script has been removed, and the `autohelp.py`
|
||||
and `diff_branches.py` scripts no longer build config file documentation
|
||||
for the swift project. The swift dev team had been manually maintaining
|
||||
their config files in-tree and to avoid duplication, doc and swift have
|
||||
agreed to link the config ref out to the dev docs. As such, there is
|
||||
therefore no reason to keep this tooling around.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
fixes:
|
||||
- Rework install guide translation build tool
|
||||
to process toctree for each distribution dynamically.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- The autohelp tools export now also RST tables.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
features:
|
||||
- New command ``openstack-indexpage`` to only generate the HTML
|
||||
index page. The index page layout has also been improved.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- extract_swift_flags.py now reads the options help strings from RST tables.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- Use jinja templating system to generate configuration reference tables.
|
@ -1,4 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- Unsupport OpenStackClient in CLI Reference
|
||||
in favor of docs in the OSC project itself.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- autohelp.py update will create the flagmappings file if it doesn't exist yet.
|
@ -1,3 +0,0 @@
|
||||
---
|
||||
other:
|
||||
- The outdated virtual build and test environment has been removed.
|
@ -1,279 +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.
|
||||
|
||||
# This file is execfile()d with the current directory set to its
|
||||
# containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
# sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration ------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
# needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be
|
||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||
# ones.
|
||||
extensions = [
|
||||
'openstackdocstheme',
|
||||
'reno.sphinxext',
|
||||
]
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
# source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
repository_name = 'openstack/openstack-doc-tools'
|
||||
bug_tag = u'openstack-doc-tools'
|
||||
project = u'OpenStack-doc-tools Release Notes'
|
||||
copyright = u'2015-2017, OpenStack Documentation team'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
from os_doc_tools.version import version_info as doc_tools_version
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = doc_tools_version.version_string_with_vcs()
|
||||
# The short X.Y version.
|
||||
version = doc_tools_version.canonical_version_string()
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
# language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
# today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
# today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all
|
||||
# documents.
|
||||
# default_role = None
|
||||
|
||||
# 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
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
# show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
# modindex_common_prefix = []
|
||||
|
||||
# If true, keep warnings as "system message" paragraphs in the built documents.
|
||||
# keep_warnings = False
|
||||
|
||||
|
||||
# -- Options for HTML output ----------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'openstackdocs'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
# html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
# html_title = None
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
# html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
# html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
# html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
# .htaccess) here, relative to this directory. These files are copied
|
||||
# directly to the root of the documentation.
|
||||
# html_extra_path = []
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
# html_last_updated_fmt = '%b %d, %Y'
|
||||
html_last_updated_fmt = '%Y-%m-%d %H:%M'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
# html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
# html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
# html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
# html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
# html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
# html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
# html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
# html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
# html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
# html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'OpenStack-Doc-Tools-ReleaseNotesdoc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output ---------------------------------------------
|
||||
|
||||
latex_elements = {
|
||||
# The paper size ('letterpaper' or 'a4paper').
|
||||
# 'papersize': 'letterpaper',
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
# 'pointsize': '10pt',
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
# 'preamble': '',
|
||||
}
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title,
|
||||
# author, documentclass [howto, manual, or own class]).
|
||||
latex_documents = [
|
||||
('index', 'OpenStack-Doc-Tools-ReleaseNotes.tex',
|
||||
u'OpenStack-Doc-Tools Release Notes Documentation',
|
||||
u'Documentation Team', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
# latex_logo = None
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
# latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
# latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# latex_show_urls = False
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output ---------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'openstack-doc-tools-releasenotes',
|
||||
u'OpenStack-Doc-Tools Release Notes Documentation',
|
||||
[u'Documentation team'], 1)
|
||||
]
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
# man_show_urls = False
|
||||
|
||||
|
||||
# -- Options for Texinfo output -------------------------------------------
|
||||
|
||||
# Grouping the document tree into Texinfo files. List of tuples
|
||||
# (source start file, target name, title, author,
|
||||
# dir menu entry, description, category)
|
||||
texinfo_documents = [
|
||||
('index', 'OpenStack-Doc-Tools-ReleaseNotes',
|
||||
u'OpenStack-Doc-Tools Release Notes Documentation',
|
||||
u'Documentation Team', 'OpenStack-Doc-Tools-ReleaseNotes',
|
||||
'One line description of project.',
|
||||
'Miscellaneous'),
|
||||
]
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
# texinfo_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
# texinfo_domain_indices = True
|
||||
|
||||
# How to display URL addresses: 'footnote', 'no', or 'inline'.
|
||||
# texinfo_show_urls = 'footnote'
|
||||
|
||||
# If true, do not generate a @detailmenu in the "Top" node's menu.
|
||||
# texinfo_no_detailmenu = False
|
||||
|
||||
# -- Options for Internationalization output ------------------------------
|
||||
locale_dirs = ['locale/']
|
@ -1,5 +0,0 @@
|
||||
==================================
|
||||
OpenStack Doc Tools Release Notes
|
||||
==================================
|
||||
|
||||
.. release-notes::
|
@ -1,13 +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!=2.1.0,>=2.0.0 # Apache-2.0
|
||||
iso8601>=0.1.11 # MIT
|
||||
lxml!=3.7.0,>=2.3 # BSD
|
||||
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
|
||||
docutils>=0.11 # OSI-Approved Open Source, Public Domain
|
||||
sphinx>=1.6.2 # BSD
|
||||
demjson # GLGPLv3+
|
||||
PyYAML>=3.10.0 # MIT
|
||||
cliff-tablib>=1.0 # Apache-2.0
|
53
setup.cfg
53
setup.cfg
@ -1,53 +0,0 @@
|
||||
[metadata]
|
||||
name = openstack-doc-tools
|
||||
summary = Tools for OpenStack Documentation
|
||||
description-file =
|
||||
README.rst
|
||||
author = OpenStack Documentation
|
||||
author-email = openstack-doc@lists.openstack.org
|
||||
home-page = http://www.openstack.org/
|
||||
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.4
|
||||
Programming Language :: Python :: 3.5
|
||||
|
||||
[files]
|
||||
packages =
|
||||
os_doc_tools
|
||||
autogenerate_config_docs
|
||||
data_files =
|
||||
share/openstack-doc-tools/sitemap = sitemap/*
|
||||
share/openstack-doc-tools/cleanup = cleanup/*
|
||||
scripts =
|
||||
bin/doc-tools-check-languages
|
||||
bin/doc-tools-update-cli-reference
|
||||
bin/doc-tools-build-rst
|
||||
|
||||
[global]
|
||||
setup-hooks =
|
||||
pbr.hooks.setup_hook
|
||||
|
||||
[entry_points]
|
||||
console_scripts =
|
||||
openstack-auto-commands = os_doc_tools.commands:main
|
||||
openstack-jsoncheck = os_doc_tools.jsoncheck:main
|
||||
openstack-indexpage = os_doc_tools.index:main
|
||||
|
||||
[build_sphinx]
|
||||
source-dir = doc/source
|
||||
build-dir = doc/build
|
||||
all_files = 1
|
||||
|
||||
[upload_sphinx]
|
||||
upload-dir = doc/build/html
|
||||
|
||||
[wheel]
|
||||
universal = 1
|
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>=2.0.0'],
|
||||
pbr=True)
|
@ -1,81 +0,0 @@
|
||||
=================
|
||||
Sitemap Generator
|
||||
=================
|
||||
|
||||
This script crawls all available sites on http://docs.openstack.org and
|
||||
extracts all URLs. Based on the URLs the script generates a sitemap for search
|
||||
engines according to the `sitemaps protocol
|
||||
<http://www.sitemaps.org/protocol.html>`_.
|
||||
|
||||
Installation
|
||||
~~~~~~~~~~~~
|
||||
|
||||
To install the needed modules you can use pip or the package management system
|
||||
included in your distribution. When using the package management system maybe
|
||||
the name of the packages differ. Installation in a virtual environment is
|
||||
recommended.
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ virtualenv venv
|
||||
$ source venv/bin/activate
|
||||
$ pip install -r requirements.txt
|
||||
|
||||
When using pip, you may also need to install some development packages. For
|
||||
example, on Ubuntu 16.04 install the following packages:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ sudo apt install gcc libssl-dev python-dev python-virtualenv
|
||||
|
||||
Usage
|
||||
~~~~~
|
||||
|
||||
To generate a new sitemap file, change into your local clone of the
|
||||
``openstack/openstack-doc-tools`` repository and run the following commands:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cd sitemap
|
||||
$ scrapy crawl sitemap
|
||||
|
||||
The script takes several minutes to crawl all available
|
||||
sites on http://docs.openstack.org. The result is available in the
|
||||
``sitemap_docs.openstack.org.xml`` file.
|
||||
|
||||
Options
|
||||
~~~~~~~
|
||||
|
||||
domain=URL
|
||||
|
||||
Sets the ``domain`` to crawl. Default is ``docs.openstack.org``.
|
||||
|
||||
For example, to crawl http://developer.openstack.org use the following
|
||||
command:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scrapy crawl sitemap -a domain=developer.openstack.org
|
||||
|
||||
The result is available in the ``sitemap_developer.openstack.org.xml`` file.
|
||||
|
||||
urls=URL
|
||||
|
||||
You can define a set of additional start URLs using the ``urls`` attribute.
|
||||
Separate multiple URLs with ``,``.
|
||||
|
||||
For example:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scrapy crawl sitemap -a domain=developer.openstack.org -a urls="http://developer.openstack.org/de/api-guide/quick-start/"
|
||||
|
||||
LOG_FILE=FILE
|
||||
|
||||
Write log messages to the specified file.
|
||||
|
||||
For example, to write to ``scrapy.log``:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ scrapy crawl sitemap -s LOG_FILE=scrapy.log
|
@ -1,90 +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 lxml
|
||||
import scrapy
|
||||
from scrapy import exporters
|
||||
|
||||
|
||||
class SitemapItemExporter(exporters.XmlItemExporter):
|
||||
'''XmlItemExporer with adjusted attributes for the root element.'''
|
||||
|
||||
def start_exporting(self):
|
||||
'''Set namespace / schema attributes for the root element.'''
|
||||
self.xg.startDocument()
|
||||
self.xg.startElement(self.root_element, {
|
||||
"xmlns": "http://www.sitemaps.org/schemas/sitemap/0.9",
|
||||
"xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
|
||||
"xsi:schemaLocation":
|
||||
"http://www.sitemaps.org/schemas/sitemap/0.9 "
|
||||
"http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
|
||||
})
|
||||
|
||||
|
||||
class IgnoreDuplicateUrls(object):
|
||||
'''Ignore duplicated URLs.'''
|
||||
|
||||
def __init__(self):
|
||||
self.processed = set()
|
||||
|
||||
def process_item(self, item, spider):
|
||||
'''Check if a URL was already found.'''
|
||||
if item['loc'] in self.processed:
|
||||
raise scrapy.exceptions.DropItem("Duplicate URL found: %s."
|
||||
% item['loc'])
|
||||
else:
|
||||
self.processed.add(item['loc'])
|
||||
return item
|
||||
|
||||
|
||||
class ExportSitemap(object):
|
||||
'''Write found URLs to a sitemap file.
|
||||
|
||||
Based on http://doc.scrapy.org/en/latest/topics/exporters.html.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
self.files = {}
|
||||
self.exporter = None
|
||||
|
||||
@classmethod
|
||||
def from_crawler(cls, crawler):
|
||||
pipeline = cls()
|
||||
crawler.signals.connect(pipeline.spider_opened,
|
||||
scrapy.signals.spider_opened)
|
||||
crawler.signals.connect(pipeline.spider_closed,
|
||||
scrapy.signals.spider_closed)
|
||||
return pipeline
|
||||
|
||||
def spider_opened(self, spider):
|
||||
output = open(os.path.join(os.getcwd(), 'sitemap_%s.xml'
|
||||
% spider.domain), 'w')
|
||||
self.files[spider] = output
|
||||
self.exporter = SitemapItemExporter(output, item_element='url',
|
||||
root_element='urlset')
|
||||
self.exporter.start_exporting()
|
||||
|
||||
def spider_closed(self, spider):
|
||||
self.exporter.finish_exporting()
|
||||
output = self.files.pop(spider)
|
||||
output.close()
|
||||
tree = lxml.etree.parse(os.path.join(os.getcwd(), "sitemap_%s.xml"
|
||||
% spider.domain))
|
||||
with open(os.path.join(os.getcwd(), "sitemap_%s.xml" % spider.domain),
|
||||
'w') as pretty:
|
||||
pretty.write(lxml.etree.tostring(tree, pretty_print=True))
|
||||
|
||||
def process_item(self, item, spider):
|
||||
self.exporter.export_item(item)
|
||||
return item
|
@ -1,33 +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.
|
||||
|
||||
# Configuration variables used inside Scrapy to enable modules/pipelines
|
||||
# and to affect the behavior of several parts.
|
||||
|
||||
from scrapy import linkextractors
|
||||
|
||||
BOT_NAME = 'sitemap'
|
||||
SPIDER_MODULES = ['generator.spiders']
|
||||
ITEM_PIPELINES = {
|
||||
'generator.pipelines.IgnoreDuplicateUrls': 500,
|
||||
'generator.pipelines.ExportSitemap': 100,
|
||||
}
|
||||
CONCURRENT_REQUESTS = 32
|
||||
CONCURRENT_REQUESTS_PER_DOMAIN = 32
|
||||
CONCURRENT_REQUESTS_PER_IP = 32
|
||||
DOWNLOAD_WARNSIZE = 67108864
|
||||
LOG_LEVEL = 'INFO'
|
||||
LOGGING_ENABLED = True
|
||||
RANDOMIZE_DOWNLOAD_DELAY = False
|
||||
ROBOTSTXT_OBEY = True
|
||||
TELNETCONSOLE_ENABLED = False
|
||||
linkextractors.IGNORED_EXTENSIONS.remove('pdf')
|
@ -1,103 +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 time
|
||||
try:
|
||||
import urlparse
|
||||
except ImportError:
|
||||
import urllib.parse as urlparse
|
||||
|
||||
from scrapy import item
|
||||
from scrapy.linkextractors import LinkExtractor
|
||||
from scrapy import spiders
|
||||
|
||||
|
||||
class SitemapItem(item.Item):
|
||||
'''Class to represent an item in the sitemap.'''
|
||||
loc = item.Field()
|
||||
lastmod = item.Field()
|
||||
priority = item.Field()
|
||||
changefreq = item.Field()
|
||||
|
||||
|
||||
class SitemapSpider(spiders.CrawlSpider):
|
||||
name = 'sitemap'
|
||||
old_releases = tuple(["/%s" % old_release for old_release in [
|
||||
'austin',
|
||||
'bexar',
|
||||
'cactus',
|
||||
'diablo',
|
||||
'essex',
|
||||
'folsom',
|
||||
'grizzly',
|
||||
'havana',
|
||||
'icehouse',
|
||||
'juno',
|
||||
'kilo',
|
||||
'liberty',
|
||||
'mitaka',
|
||||
'newton'
|
||||
]])
|
||||
|
||||
rules = [
|
||||
spiders.Rule(
|
||||
LinkExtractor(
|
||||
allow=[
|
||||
r'.*\.html',
|
||||
r'.*\.pdf',
|
||||
r'.*\.xml',
|
||||
r'.*\.txt',
|
||||
r'.*/',
|
||||
],
|
||||
deny=[
|
||||
r'/trunk/',
|
||||
r'/draft/',
|
||||
r'/api/',
|
||||
r'/juno/',
|
||||
r'/icehouse/'
|
||||
]
|
||||
),
|
||||
follow=True, callback='parse_item'
|
||||
)
|
||||
]
|
||||
|
||||
def __init__(self, domain='docs.openstack.org', urls='', *args, **kwargs):
|
||||
super(SitemapSpider, self).__init__(*args, **kwargs)
|
||||
self.domain = domain
|
||||
self.allowed_domains = [domain]
|
||||
self.start_urls = ['http://%s' % domain]
|
||||
for url in urls.split(','):
|
||||
if not url:
|
||||
continue
|
||||
self.start_urls.append(url)
|
||||
|
||||
def parse_item(self, response):
|
||||
item = SitemapItem()
|
||||
item['loc'] = response.url
|
||||
|
||||
path = urlparse.urlsplit(response.url).path
|
||||
if path.startswith(self.old_releases):
|
||||
# weekly changefrequency and lower priority for old files
|
||||
item['priority'] = '0.5'
|
||||
item['changefreq'] = 'weekly'
|
||||
else:
|
||||
# daily changefrequency and highest priority for current files
|
||||
item['priority'] = '1.0'
|
||||
item['changefreq'] = 'daily'
|
||||
|
||||
if 'Last-Modified' in response.headers:
|
||||
timestamp = response.headers['Last-Modified']
|
||||
else:
|
||||
timestamp = response.headers['Date']
|
||||
lastmod = time.strptime(timestamp, "%a, %d %b %Y %H:%M:%S %Z")
|
||||
item['lastmod'] = time.strftime("%Y-%m-%dT%H:%M:%S%z", lastmod)
|
||||
return item
|
@ -1,5 +0,0 @@
|
||||
[settings]
|
||||
default = generator.settings
|
||||
|
||||
[deploy]
|
||||
project = generator
|
@ -1,20 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<!-- Use this transform on a sitemap.xml file generated from freesitemapgenerator.com -->
|
||||
<!-- It removes the /trunk and /draft URLs and the release URLs that you indicate below, here it's folsom -->
|
||||
<xsl:stylesheet version="1.0"
|
||||
xmlns:sm="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<!-- template match equals any other url element, keep -->
|
||||
<xsl:template match="node() | @*">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="node() | @*"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<!-- discard any url/loc that refer to trunk or folsom -->
|
||||
<xsl:template match="sm:url[starts-with(./sm:loc,'http://docs.openstack.org/trunk')]"/>
|
||||
<xsl:template match="sm:url[starts-with(./sm:loc,'http://docs.openstack.org/draft')]"/>
|
||||
<xsl:template match="sm:url[starts-with(./sm:loc,'http://docs.openstack.org/folsom')]"/>
|
||||
|
||||
</xsl:stylesheet>
|
@ -1,18 +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 already pins down pep8, pyflakes and flake8
|
||||
hacking!=0.13.0,<0.14,>=0.12.0 # Apache-2.0
|
||||
|
||||
bashate>=0.2 # Apache-2.0
|
||||
doc8 # Apache-2.0
|
||||
|
||||
pylint==1.7.1 # GPLv2
|
||||
|
||||
reno!=2.3.1,>=1.8.0 # Apache-2.0
|
||||
openstackdocstheme>=1.11.0 # Apache-2.0
|
||||
testrepository>=0.0.18 # Apache-2.0/BSD
|
||||
|
||||
# mock object framework
|
||||
mock>=2.0 # BSD
|
@ -1,37 +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 mock
|
||||
from os_doc_tools import index
|
||||
import unittest
|
||||
|
||||
|
||||
class TestGenerateIndex(unittest.TestCase):
|
||||
def test_dir_created(self):
|
||||
path = 'path'
|
||||
with mock.patch.object(index, 'open'):
|
||||
with mock.patch.object(index.os, 'mkdir') as mock_mkdir:
|
||||
index.generate_index_file(path)
|
||||
self.assertTrue(mock_mkdir.called)
|
||||
|
||||
def test_dir_not_created_when_exists(self):
|
||||
path = 'path'
|
||||
with mock.patch.object(index, 'open'):
|
||||
with mock.patch.object(index.os, 'mkdir') as mock_mkdir:
|
||||
with mock.patch.object(index.os.path, 'isdir',
|
||||
returned_value=True):
|
||||
index.generate_index_file(path)
|
||||
self.assertFalse(mock_mkdir.called)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,98 +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 mock
|
||||
from os_doc_tools import jsoncheck
|
||||
import unittest
|
||||
|
||||
|
||||
class MockOpen(object):
|
||||
|
||||
def read(self):
|
||||
return "raw"
|
||||
|
||||
def write(self):
|
||||
return True
|
||||
|
||||
|
||||
class TestFileFunctions(unittest.TestCase):
|
||||
|
||||
def test_indent_note(self):
|
||||
note = "Hello\nWorld"
|
||||
with mock.patch.object(jsoncheck.textwrap, 'fill') as mock_fill:
|
||||
mock_fill.return_value = "Hello World"
|
||||
jsoncheck._indent_note(note)
|
||||
mock_fill.assert_any_call('Hello', initial_indent=' ',
|
||||
subsequent_indent=' ',
|
||||
width=80)
|
||||
mock_fill.assert_any_call('World', initial_indent=' ',
|
||||
subsequent_indent=' ',
|
||||
width=80)
|
||||
|
||||
def test_get_demjson_diagnostics(self):
|
||||
raw = "raw"
|
||||
|
||||
with mock.patch.object(jsoncheck.demjson, 'decode', return_value=True):
|
||||
errstr = jsoncheck._get_demjson_diagnostics(raw)
|
||||
self.assertTrue(errstr is None)
|
||||
|
||||
with mock.patch.object(jsoncheck.demjson, 'decode') as mock_decode:
|
||||
mock_decode.side_effect = jsoncheck.demjson.JSONError(raw)
|
||||
errstr = jsoncheck._get_demjson_diagnostics(raw)
|
||||
expected_error_str = " Error: raw"
|
||||
self.assertEqual(errstr, expected_error_str)
|
||||
|
||||
def test_parse_json(self):
|
||||
raw = "raw"
|
||||
with mock.patch.object(jsoncheck.json, 'loads',
|
||||
return_value="Success"):
|
||||
parsed = jsoncheck._parse_json(raw)
|
||||
self.assertEqual(parsed, "Success")
|
||||
|
||||
with mock.patch.object(jsoncheck.json, 'loads') as mock_loads:
|
||||
mock_loads.side_effect = ValueError()
|
||||
with self.assertRaises(jsoncheck.ParserException):
|
||||
parsed = jsoncheck._parse_json(raw)
|
||||
|
||||
def test_format_parsed_json(self):
|
||||
with mock.patch.object(jsoncheck.json, 'dumps') as mock_dumps:
|
||||
mock_dumps.return_value = "Success"
|
||||
returned_value = jsoncheck._format_parsed_json('raw')
|
||||
self.assertEqual(returned_value, "Success\n")
|
||||
self.assertTrue(mock_dumps.called)
|
||||
|
||||
def test_process_file(self):
|
||||
with mock.patch.object(jsoncheck, 'open', returned_value=MockOpen()):
|
||||
with mock.patch.object(jsoncheck, '_parse_json') as mock_parse:
|
||||
mock_parse.side_effect = jsoncheck.ParserException
|
||||
with self.assertRaises(ValueError):
|
||||
jsoncheck._process_file('path')
|
||||
|
||||
with mock.patch.object(jsoncheck, 'open', returned_value=MockOpen()):
|
||||
with mock.patch.object(jsoncheck, '_parse_json',
|
||||
returned_value="Success"):
|
||||
with mock.patch.object(jsoncheck, '_format_parsed_json',
|
||||
returned_value="not_raw"):
|
||||
with self.assertRaises(ValueError):
|
||||
jsoncheck._process_file('path', 'check')
|
||||
|
||||
with mock.patch.object(jsoncheck, 'open', returned_value=MockOpen()):
|
||||
with mock.patch.object(jsoncheck, '_parse_json',
|
||||
returned_value="Success"):
|
||||
with mock.patch.object(jsoncheck, '_format_parsed_json',
|
||||
returned_value="not_raw"):
|
||||
with self.assertRaises(ValueError):
|
||||
jsoncheck._process_file('path', 'formatting')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,197 +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 mock
|
||||
from sitemap.generator import pipelines
|
||||
import unittest
|
||||
|
||||
|
||||
class TestSitemapItemExporter(unittest.TestCase):
|
||||
|
||||
def test_start_exporting(self):
|
||||
output = mock.MagicMock()
|
||||
itemExplorer = pipelines.SitemapItemExporter(output)
|
||||
|
||||
with mock.patch.object(itemExplorer.xg, 'startDocument',
|
||||
return_value=None) as mock_start_document:
|
||||
with mock.patch.object(itemExplorer.xg, 'startElement',
|
||||
return_value=None) as mock_start_element:
|
||||
itemExplorer.start_exporting()
|
||||
|
||||
self.assertTrue(mock_start_document.called)
|
||||
self.assertTrue(mock_start_element.called)
|
||||
|
||||
|
||||
class TestIgnoreDuplicateUrls(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.ignore_urls = pipelines.IgnoreDuplicateUrls()
|
||||
|
||||
def test_set_is_set_at_init(self):
|
||||
self.assertIsInstance(self.ignore_urls.processed, set)
|
||||
|
||||
def test_set_is_empty_at_init(self):
|
||||
self.assertEqual(len(self.ignore_urls.processed), 0)
|
||||
|
||||
def test_duplicate_url(self):
|
||||
self.ignore_urls.processed.add('url')
|
||||
item = {'loc': 'url'}
|
||||
spider = mock.MagicMock()
|
||||
|
||||
with self.assertRaises(pipelines.scrapy.exceptions.DropItem):
|
||||
self.ignore_urls.process_item(item, spider)
|
||||
|
||||
def test_url_added_to_processed(self):
|
||||
self.assertFalse('url' in self.ignore_urls.processed)
|
||||
|
||||
item = {'loc': 'url'}
|
||||
spider = mock.MagicMock()
|
||||
self.ignore_urls.process_item(item, spider)
|
||||
self.assertTrue('url' in self.ignore_urls.processed)
|
||||
|
||||
def test_item_is_returned(self):
|
||||
item = {'loc': 'url'}
|
||||
spider = mock.MagicMock()
|
||||
returned_item = self.ignore_urls.process_item(item, spider)
|
||||
self.assertEqual(item, returned_item)
|
||||
|
||||
|
||||
class TestExportSitemap(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.export_sitemap = pipelines.ExportSitemap()
|
||||
self.spider = mock.MagicMock()
|
||||
|
||||
def test_variables_set_at_init(self):
|
||||
self.assertIsInstance(self.export_sitemap.files, dict)
|
||||
self.assertTrue(self.export_sitemap.exporter is None)
|
||||
|
||||
def test_spider_opened_calls_open(self):
|
||||
with mock.patch.object(pipelines, 'open',
|
||||
return_value=None) as mocked_open:
|
||||
with mock.patch.object(pipelines, 'SitemapItemExporter'):
|
||||
self.export_sitemap.spider_opened(self.spider)
|
||||
|
||||
self.assertTrue(mocked_open.called)
|
||||
|
||||
def test_spider_opened_assigns_spider(self):
|
||||
prev_len = len(self.export_sitemap.files)
|
||||
with mock.patch.object(pipelines, 'open', return_value=None):
|
||||
with mock.patch.object(pipelines, 'SitemapItemExporter'):
|
||||
self.export_sitemap.spider_opened(self.spider)
|
||||
|
||||
after_len = len(self.export_sitemap.files)
|
||||
self.assertTrue(after_len - prev_len, 1)
|
||||
|
||||
def test_spider_opened_instantiates_exporter(self):
|
||||
with mock.patch.object(pipelines, 'open', return_value=None):
|
||||
with mock.patch.object(pipelines,
|
||||
'SitemapItemExporter') as mocked_exporter:
|
||||
self.export_sitemap.spider_opened(self.spider)
|
||||
|
||||
self.assertTrue(mocked_exporter.called)
|
||||
|
||||
def test_spider_opened_exporter_starts_exporting(self):
|
||||
with mock.patch.object(pipelines, 'open', return_value=None):
|
||||
with mock.patch.object(pipelines.SitemapItemExporter,
|
||||
'start_exporting') as mocked_start:
|
||||
self.export_sitemap.spider_opened(self.spider)
|
||||
|
||||
self.assertTrue(mocked_start.called)
|
||||
|
||||
def test_spider_closed_calls_finish(self):
|
||||
self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.exporter.finish_exporting = mock.MagicMock()
|
||||
self.export_sitemap.files[self.spider] = mock.MagicMock()
|
||||
|
||||
with mock.patch.object(pipelines, 'lxml'):
|
||||
with mock.patch.object(pipelines, 'open'):
|
||||
self.export_sitemap.spider_closed(self.spider)
|
||||
|
||||
self.assertTrue(self.export_sitemap.exporter.finish_exporting.called)
|
||||
|
||||
def test_spider_closed_pops_spider(self):
|
||||
self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.files[self.spider] = mock.MagicMock()
|
||||
|
||||
self.assertTrue(self.spider in self.export_sitemap.files)
|
||||
|
||||
with mock.patch.object(pipelines, 'lxml'):
|
||||
with mock.patch.object(pipelines, 'open'):
|
||||
self.export_sitemap.spider_closed(self.spider)
|
||||
|
||||
self.assertFalse(self.spider in self.export_sitemap.files)
|
||||
|
||||
def test_spider_closed_parses_with_lxml(self):
|
||||
self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.exporter.finish_exporting = mock.MagicMock()
|
||||
self.export_sitemap.files[self.spider] = mock.MagicMock()
|
||||
|
||||
with mock.patch.object(pipelines.lxml, 'etree'):
|
||||
with mock.patch.object(pipelines.lxml.etree,
|
||||
'parse') as mocked_lxml_parse:
|
||||
with mock.patch.object(pipelines, 'open'):
|
||||
self.export_sitemap.spider_closed(self.spider)
|
||||
|
||||
self.assertTrue(mocked_lxml_parse.called)
|
||||
|
||||
def test_spider_closed_opens_xml_files(self):
|
||||
self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.exporter.finish_exporting = mock.MagicMock()
|
||||
self.export_sitemap.files[self.spider] = mock.MagicMock()
|
||||
|
||||
with mock.patch.object(pipelines, 'lxml'):
|
||||
with mock.patch.object(pipelines, 'open') as mocked_open:
|
||||
self.export_sitemap.spider_closed(self.spider)
|
||||
|
||||
self.assertTrue(mocked_open.called)
|
||||
|
||||
def test_spider_closed_writes_tree(self):
|
||||
self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.exporter.finish_exporting = mock.MagicMock()
|
||||
self.export_sitemap.files[self.spider] = mock.MagicMock()
|
||||
|
||||
with mock.patch.object(pipelines.lxml, 'etree'):
|
||||
with mock.patch.object(pipelines.lxml.etree,
|
||||
'tostring') as mocked_lxml_tostring:
|
||||
with mock.patch.object(pipelines, 'open'):
|
||||
self.export_sitemap.spider_closed(self.spider)
|
||||
|
||||
self.assertTrue(mocked_lxml_tostring.called)
|
||||
|
||||
def test_process_item_exports_item(self):
|
||||
item = spider = self.export_sitemap.exporter = mock.MagicMock()
|
||||
self.export_sitemap.exporter.export_item = mock.MagicMock()
|
||||
self.export_sitemap.process_item(item, spider)
|
||||
|
||||
self.assertTrue(self.export_sitemap.exporter.export_item.called)
|
||||
|
||||
def test_process_item_returns_item(self):
|
||||
spider = self.export_sitemap.exporter = mock.MagicMock()
|
||||
item = {'random': 'item'}
|
||||
returned_item = self.export_sitemap.process_item(item, spider)
|
||||
|
||||
self.assertEqual(item, returned_item)
|
||||
|
||||
def test_from_crawler_exists(self):
|
||||
attr_exists = hasattr(pipelines.ExportSitemap, 'from_crawler')
|
||||
attr_callable = callable(getattr(pipelines.ExportSitemap,
|
||||
'from_crawler'))
|
||||
self.assertTrue(attr_exists and attr_callable)
|
||||
|
||||
def test_from_crawler_assigns_pipeline(self):
|
||||
crawler = mock.MagicMock()
|
||||
pipelines.ExportSitemap.from_crawler(crawler)
|
||||
# still thinking how to go about here.
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,132 +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 mock
|
||||
import scrapy
|
||||
from sitemap.generator.spiders import sitemap_file
|
||||
import unittest
|
||||
|
||||
|
||||
class TestSitemapItem(unittest.TestCase):
|
||||
|
||||
def test_class_type(self):
|
||||
self.assertTrue(type(sitemap_file.SitemapItem) is scrapy.item.ItemMeta)
|
||||
|
||||
def test_class_supports_fields(self):
|
||||
with mock.patch.object(scrapy.item, 'Field'):
|
||||
a = sitemap_file.SitemapItem()
|
||||
|
||||
supported_fields = ['loc', 'lastmod', 'priority', 'changefreq']
|
||||
for field in supported_fields:
|
||||
a[field] = field
|
||||
|
||||
not_supported_fields = ['some', 'random', 'fields']
|
||||
for field in not_supported_fields:
|
||||
with self.assertRaises(KeyError):
|
||||
a[field] = field
|
||||
|
||||
|
||||
class TestSitemapSpider(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.spider = sitemap_file.SitemapSpider()
|
||||
|
||||
def test_set_vars_on_init(self):
|
||||
domain = 'docs.openstack.org'
|
||||
self.assertEqual(self.spider.domain, domain)
|
||||
self.assertEqual(self.spider.allowed_domains, [domain])
|
||||
self.assertEqual(self.spider.start_urls, ['http://%s' % domain])
|
||||
|
||||
def test_start_urls_get_appended(self):
|
||||
urls = 'new.openstack.org, old.openstack.org'
|
||||
urls_len = len(urls.split(','))
|
||||
spider_len = len(self.spider.start_urls)
|
||||
|
||||
spider_with_urls = sitemap_file.SitemapSpider(urls=urls)
|
||||
spider_with_urls_len = len(spider_with_urls.start_urls)
|
||||
|
||||
self.assertEqual(spider_with_urls_len, (urls_len + spider_len))
|
||||
|
||||
def test_parse_items_inits_sitemap(self):
|
||||
response = mock.MagicMock()
|
||||
with mock.patch.object(sitemap_file,
|
||||
'SitemapItem') as mocked_sitemap_item:
|
||||
with mock.patch.object(sitemap_file.urlparse,
|
||||
'urlsplit'):
|
||||
with mock.patch.object(sitemap_file, 'time'):
|
||||
self.spider.parse_item(response)
|
||||
|
||||
self.assertTrue(mocked_sitemap_item.called)
|
||||
|
||||
def test_parse_items_gets_path(self):
|
||||
response = mock.MagicMock()
|
||||
with mock.patch.object(sitemap_file, 'SitemapItem'):
|
||||
with mock.patch.object(sitemap_file.urlparse,
|
||||
'urlsplit') as mocked_urlsplit:
|
||||
with mock.patch.object(sitemap_file, 'time'):
|
||||
self.spider.parse_item(response)
|
||||
|
||||
self.assertTrue(mocked_urlsplit.called)
|
||||
|
||||
def test_parse_items_low_priority_weekly_freq(self):
|
||||
response = mock.MagicMock()
|
||||
path = sitemap_file.urlparse.SplitResult(
|
||||
scheme='https',
|
||||
netloc='docs.openstack.com',
|
||||
path='/mitaka',
|
||||
query='',
|
||||
fragment=''
|
||||
)
|
||||
with mock.patch.object(sitemap_file.urlparse, 'urlsplit',
|
||||
return_value=path):
|
||||
with mock.patch.object(sitemap_file, 'time'):
|
||||
returned_item = self.spider.parse_item(response)
|
||||
|
||||
self.assertEqual('0.5', returned_item['priority'])
|
||||
self.assertEqual('weekly', returned_item['changefreq'])
|
||||
|
||||
def test_parse_items_high_priority_daily_freq(self):
|
||||
response = mock.MagicMock()
|
||||
path = sitemap_file.urlparse.SplitResult(
|
||||
scheme='https',
|
||||
netloc='docs.openstack.com',
|
||||
path='/ocata',
|
||||
query='',
|
||||
fragment=''
|
||||
)
|
||||
with mock.patch.object(sitemap_file.urlparse, 'urlsplit',
|
||||
return_value=path):
|
||||
with mock.patch.object(sitemap_file, 'time'):
|
||||
returned_item = self.spider.parse_item(response)
|
||||
|
||||
self.assertEqual('1.0', returned_item['priority'])
|
||||
self.assertEqual('daily', returned_item['changefreq'])
|
||||
|
||||
def test_parse_returns_populated_item(self):
|
||||
response = mock.MagicMock()
|
||||
path = sitemap_file.urlparse.SplitResult(
|
||||
scheme='https',
|
||||
netloc='docs.openstack.com',
|
||||
path='/ocata',
|
||||
query='',
|
||||
fragment=''
|
||||
)
|
||||
with mock.patch.object(sitemap_file.urlparse, 'urlsplit',
|
||||
return_value=path):
|
||||
with mock.patch.object(sitemap_file, 'time'):
|
||||
returned_item = self.spider.parse_item(response)
|
||||
|
||||
self.assertEqual(4, len(returned_item))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Client constraint file contains this client version pin that is in conflict
|
||||
# with installing the client from source. We should remove the version pin in
|
||||
# the constraints file before applying it for from-source installation.
|
||||
|
||||
CONSTRAINTS_FILE=$1
|
||||
shift 1
|
||||
|
||||
set -e
|
||||
|
||||
# NOTE(tonyb): Place this in the tox enviroment's log dir so it will get
|
||||
# published to logs.openstack.org for easy debugging.
|
||||
localfile="$VIRTUAL_ENV/log/upper-constraints.txt"
|
||||
|
||||
if [[ $CONSTRAINTS_FILE != http* ]]; then
|
||||
CONSTRAINTS_FILE=file://$CONSTRAINTS_FILE
|
||||
fi
|
||||
# NOTE(tonyb): need to add curl to bindep.txt if the project supports bindep
|
||||
curl $CONSTRAINTS_FILE --insecure --progress-bar --output $localfile
|
||||
|
||||
pip install -c$localfile openstack-requirements
|
||||
|
||||
# This is the main purpose of the script: Allow local installation of
|
||||
# the current repo. It is listed in constraints file and thus any
|
||||
# install will be constrained and we need to unconstrain it.
|
||||
edit-constraints $localfile -- $CLIENT_NAME
|
||||
|
||||
pip install -c$localfile -U $*
|
||||
exit $?
|
64
tox.ini
64
tox.ini
@ -1,64 +0,0 @@
|
||||
[tox]
|
||||
minversion = 2.0
|
||||
envlist = py3,py27,pep8
|
||||
skipsdist = True
|
||||
|
||||
[testenv]
|
||||
usedevelop = True
|
||||
install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages}
|
||||
setenv =
|
||||
VIRTUAL_ENV={envdir}
|
||||
CLIENT_NAME=openstack-doc-tools
|
||||
# Install also sitemap scraping tool, not installed by default
|
||||
# therefore not in requirements file
|
||||
deps = scrapy>=1.0.0
|
||||
-r{toxinidir}/test-requirements.txt
|
||||
-r{toxinidir}/requirements.txt
|
||||
commands = python setup.py testr --slowest --testr-args='{posargs}'
|
||||
|
||||
[testenv:pep8]
|
||||
commands =
|
||||
flake8
|
||||
# Run doc8 to check .rst and .txt files.
|
||||
# HACKING.rst is the only file that is not referenced from
|
||||
# doc/source, so add it explicitly.
|
||||
doc8 -e txt -e rst doc/source/ HACKING.rst
|
||||
# Run bashate during pep8 runs to ensure violations are caught by
|
||||
# the check and gate queues.
|
||||
bashate autogenerate_config_docs/autohelp-wrapper \
|
||||
bin/doc-tools-check-languages \
|
||||
cleanup/remove_trailing_whitespaces.sh
|
||||
|
||||
[testenv:pylint]
|
||||
commands = pylint os_doc_tools cleanup sitemap
|
||||
|
||||
[testenv:releasenotes]
|
||||
commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html
|
||||
|
||||
[testenv:sitemap]
|
||||
# commands = functional test command goes here
|
||||
|
||||
[testenv:venv]
|
||||
commands = {posargs}
|
||||
|
||||
[testenv:docs]
|
||||
commands = python setup.py build_sphinx
|
||||
|
||||
[testenv:bindep]
|
||||
# Do not install any requirements. We want this to be fast and work even if
|
||||
# system dependencies are missing, since it's used to tell you what system
|
||||
# dependencies are missing! This also means that bindep must be installed
|
||||
# separately, outside of the requirements files, and develop mode disabled
|
||||
# explicitly to avoid unnecessarily installing the checked-out repo too (this
|
||||
# further relies on "tox.skipsdist = True" above).
|
||||
deps = bindep
|
||||
commands = bindep test
|
||||
usedevelop = False
|
||||
|
||||
[flake8]
|
||||
show-source = True
|
||||
builtins = _
|
||||
exclude=.venv,.git,.tox,dist,*lib/python*,*egg,build,*autogenerate_config_docs/venv,*autogenerate_config_docs/sources,doc/source/conf.py
|
||||
# 28 is currently the most complex thing we have
|
||||
max-complexity=29
|
||||
ignore = H101
|
Loading…
Reference in New Issue
Block a user