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: Idb6639333fd4d976be89a151d8ef32cf1e9def89
This commit is contained in:
Tony Breeds
2017-09-12 15:38:14 -06:00
parent 089e8e64b1
commit a97ace7eb8
30 changed files with 14 additions and 4940 deletions

15
.gitignore vendored
View File

@@ -1,15 +0,0 @@
build
dist
git_review.egg-info
MANIFEST
AUTHORS
ChangeLog
.eggs
.gerrit
.testrepository
.tox
.venv
*.egg
*.egg-info
*.pyc
doc/build

View File

@@ -1,4 +0,0 @@
[gerrit]
host=review.openstack.org
port=29418
project=openstack-infra/git-review.git

View File

@@ -1,9 +0,0 @@
# Format is:
# <preferred e-mail> <other e-mail 1>
# <preferred e-mail> <other e-mail 2>
David Ostrovsky <david@ostrovsky.org> <David.Ostrovsky@gmx.de>
David Ostrovsky <david@ostrovsky.org> <david.ostrovsky@gmx.de>
James E. Blair <james.blair@rackspace.com> <jeblair@openstack.org>
James E. Blair <james.blair@rackspace.com> <corvus@inaugust.com>
James E. Blair <james.blair@rackspace.com> <jeblair@hp.com>

View File

@@ -1,8 +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 ./ ./git_review/tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@@ -1,22 +0,0 @@
============================
Contributing to git-review
============================
To get the latest code, see: https://git.openstack.org/cgit/openstack-infra/git-review
Bugs are handled at: https://storyboard.openstack.org/#!/project/719
There is a mailing list at: http://lists.openstack.org/cgi-bin/mailman/listinfo/openstack-infra
Code reviews, as you might expect, are handled by gerrit at:
https://review.openstack.org
See http://wiki.openstack.org/GerritWorkflow for details. Pull
requests submitted through GitHub will be ignored.
Use ``git review`` to submit patches (after creating a gerrit account
that links to your launchpad account). Example::
# Do your commits
git review
# Enter your username if prompted

View File

@@ -1,311 +0,0 @@
Hacking git-review
==================
Development of git-review is managed by OpenStack's Gerrit, which can be
found at https://review.openstack.org/
Instructions on submitting patches can be found at
http://docs.openstack.org/infra/manual/developers.html#development-workflow
git-review should, in general, not depend on a huge number of external
libraries, so that installing it is a lightweight operation.
OpenStack Style Commandments
============================
- Step 1: Read http://www.python.org/dev/peps/pep-0008/
- Step 2: Read http://www.python.org/dev/peps/pep-0008/ again
- Step 3: Read on
General
-------
- Put two newlines between top-level code (funcs, classes, etc)
- Use only UNIX style newlines ("\n"), not Windows style ("\r\n")
- Put one newline between methods in classes and anywhere else
- Long lines should be wrapped in parentheses
in preference to using a backslash for line continuation.
- Do not write "except:", use "except Exception:" at the very least
- Include your name with TODOs as in "#TODO(termie)"
- Do not shadow a built-in or reserved word. Example::
def list():
return [1, 2, 3]
mylist = list() # BAD, shadows `list` built-in
class Foo(object):
def list(self):
return [1, 2, 3]
mylist = Foo().list() # OKAY, does not shadow built-in
- Use the "is not" operator when testing for unequal identities. Example::
if not X is Y: # BAD, intended behavior is ambiguous
pass
if X is not Y: # OKAY, intuitive
pass
- Use the "not in" operator for evaluating membership in a collection. Example::
if not X in Y: # BAD, intended behavior is ambiguous
pass
if X not in Y: # OKAY, intuitive
pass
if not (X in Y or X in Z): # OKAY, still better than all those 'not's
pass
Imports
-------
- Do not import objects, only modules (*)
- Do not import more than one module per line (*)
- Do not use wildcard ``*`` import (*)
- Do not make relative imports
- Do not make new nova.db imports in nova/virt/*
- Order your imports by the full module path
- Organize your imports according to the following template
(*) exceptions are:
- imports from ``migrate`` package
- imports from ``sqlalchemy`` package
- imports from ``nova.db.sqlalchemy.session`` module
- imports from ``nova.db.sqlalchemy.migration.versioning_api`` package
Example::
# vim: tabstop=4 shiftwidth=4 softtabstop=4
{{stdlib imports in human alphabetical order}}
\n
{{third-party lib imports in human alphabetical order}}
\n
{{nova imports in human alphabetical order}}
\n
\n
{{begin your code}}
Human Alphabetical Order Examples
---------------------------------
Example::
import httplib
import logging
import random
import StringIO
import time
import unittest
import eventlet
import webob.exc
import nova.api.ec2
from nova.api import openstack
from nova.auth import users
from nova.endpoint import cloud
import nova.flags
from nova import test
Docstrings
----------
Example::
"""A one line docstring looks like this and ends in a period."""
"""A multi line docstring has a one-line summary, less than 80 characters.
Then a new paragraph after a newline that explains in more detail any
general information about the function, class or method. Example usages
are also great to have here if it is a complex class or function.
When writing the docstring for a class, an extra line should be placed
after the closing quotations. For more in-depth explanations for these
decisions see http://www.python.org/dev/peps/pep-0257/
If you are going to describe parameters and return values, use Sphinx, the
appropriate syntax is as follows.
:param foo: the foo parameter
:param bar: the bar parameter
:returns: return_type -- description of the return value
:returns: description of the return value
:raises: AttributeError, KeyError
"""
Dictionaries/Lists
------------------
If a dictionary (dict) or list object is longer than 80 characters, its items
should be split with newlines. Embedded iterables should have their items
indented. Additionally, the last item in the dictionary should have a trailing
comma. This increases readability and simplifies future diffs.
Example::
my_dictionary = {
"image": {
"name": "Just a Snapshot",
"size": 2749573,
"properties": {
"user_id": 12,
"arch": "x86_64",
},
"things": [
"thing_one",
"thing_two",
],
"status": "ACTIVE",
},
}
Calling Methods
---------------
Calls to methods 80 characters or longer should format each argument with
newlines. This is not a requirement, but a guideline::
unnecessarily_long_function_name('string one',
'string two',
kwarg1=constants.ACTIVE,
kwarg2=['a', 'b', 'c'])
Rather than constructing parameters inline, it is better to break things up::
list_of_strings = [
'what_a_long_string',
'not as long',
]
dict_of_numbers = {
'one': 1,
'two': 2,
'twenty four': 24,
}
object_one.call_a_method('string three',
'string four',
kwarg1=list_of_strings,
kwarg2=dict_of_numbers)
Internationalization (i18n) Strings
-----------------------------------
In order to support multiple languages, we have a mechanism to support
automatic translations of exception and log strings.
Example::
msg = _("An error occurred")
raise HTTPBadRequest(explanation=msg)
If you have a variable to place within the string, first internationalize the
template string then do the replacement.
Example::
msg = _("Missing parameter: %s") % ("flavor",)
LOG.error(msg)
If you have multiple variables to place in the string, use keyword parameters.
This helps our translators reorder parameters when needed.
Example::
msg = _("The server with id %(s_id)s has no key %(m_key)s")
LOG.error(msg % {"s_id": "1234", "m_key": "imageId"})
Creating Unit Tests
-------------------
For every new feature, unit tests should be created that both test and
(implicitly) document the usage of said feature. If submitting a patch for a
bug that had no unit test, a new passing unit test should be added. If a
submitted bug fix does have a unit test, be sure to add a new one that fails
without the patch and passes with the patch.
For more information on creating unit tests and utilizing the testing
infrastructure in OpenStack Nova, please read nova/tests/README.rst.
Running Tests
-------------
The testing system is based on a combination of tox and testr. The canonical
approach to running tests is to simply run the command `tox`. This will
create virtual environments, populate them with dependencies and run all of
the tests that OpenStack CI systems run. Behind the scenes, tox is running
`testr run --parallel`, but is set up such that you can supply any additional
testr arguments that are needed to tox. For example, you can run:
`tox -- --analyze-isolation` to cause tox to tell testr to add
--analyze-isolation to its argument list.
It is also possible to run the tests inside of a virtual environment
you have created, or it is possible that you have all of the dependencies
installed locally already. In this case, you can interact with the testr
command directly. Running `testr run` will run the entire test suite. `testr
run --parallel` will run it in parallel (this is the default incantation tox
uses.) More information about testr can be found at:
http://wiki.openstack.org/testr
openstack-common
----------------
A number of modules from openstack-common are imported into the project.
These modules are "incubating" in openstack-common and are kept in sync
with the help of openstack-common's update.py script. See:
http://wiki.openstack.org/CommonLibrary#Incubation
The copy of the code should never be directly modified here. Please
always update openstack-common first and then run the script to copy
the changes across.
OpenStack Trademark
-------------------
OpenStack is a registered trademark of the OpenStack Foundation, and uses the
following capitalization:
OpenStack
Commit Messages
---------------
Using a common format for commit messages will help keep our git history
readable. Follow these guidelines:
First, provide a brief summary of 50 characters or less. Summaries
of greater then 72 characters will be rejected by the gate.
The first line of the commit message should provide an accurate
description of the change, not just a reference to a bug or
blueprint. It must be followed by a single blank line.
If the change relates to a specific driver (libvirt, xenapi, qpid, etc...),
begin the first line of the commit message with the driver name, lowercased,
followed by a colon.
Following your brief summary, provide a more detailed description of
the patch, manually wrapping the text at 72 characters. This
description should provide enough detail that one does not have to
refer to external resources to determine its high-level functionality.
Once you use 'git review', two lines will be appended to the commit
message: a blank line followed by a 'Change-Id'. This is important
to correlate this commit with a specific review in Gerrit, and it
should not be modified.
For further information on constructing high quality commit messages,
and how to split up commits into a series of changes, consult the
project wiki:
http://wiki.openstack.org/GitCommitMessages

202
LICENSE
View File

@@ -1,202 +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.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -1,7 +0,0 @@
include README.rst
include LICENSE
include AUTHORS
include ChangeLog
include HACKING.rst
include git-review.1
include tox.ini

14
README Normal file
View 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.

View File

@@ -1,12 +0,0 @@
git-review
==========
A git command for submitting branches to Gerrit
git-review is a tool that helps submitting git branches to gerrit for
review.
* Free software: Apache license
* Documentation: http://docs.openstack.org/infra/git-review/
* Source: https://git.openstack.org/cgit/openstack-infra/git-review
* Bugs: https://storyboard.openstack.org/#!/project/719

View File

@@ -1,177 +0,0 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# User-friendly check for sphinx-build
ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
endif
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " xml to make Docutils-native XML files"
@echo " pseudoxml to make pseudoxml-XML files for display purposes"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/git-review.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/git-review.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/git-review"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/git-review"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
latexpdfja:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through platex and dvipdfmx..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
xml:
$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
@echo
@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
pseudoxml:
$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
@echo
@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."

View File

@@ -1,252 +0,0 @@
# -*- coding: utf-8 -*-
#
# git-review documentation build configuration file, created by
# sphinx-quickstart on Mon Dec 1 14:06:22 2014.
#
# 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.
import sys
import os
# 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 = [
'sphinx.ext.autodoc',
'oslosphinx',
]
# 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.
project = u'git-review'
copyright = u'2014, OpenStack Contributors'
# 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 = 'default'
# 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'
# 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 = 'git-reviewdoc'
# -- 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', 'git-review.tex', u'git-review Documentation',
u'OpenStack Contributors', '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', 'git-review', u'git-review Documentation',
[u'OpenStack Contributors'], 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', 'git-review', u'git-review Documentation',
u'OpenStack Contributors', 'git-review', '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

View File

@@ -1,23 +0,0 @@
.. include:: ../../CONTRIBUTING.rst
Running tests
=============
Running tests for git-review means running a local copy of Gerrit to
check that git-review interacts correctly with it. This requires the
following:
* a Java Runtime Environment on the machine to run tests on
* Internet access to download the gerrit.war file, or a locally
cached copy (it needs to be located in a .gerrit directory at the
top level of the git-review project)
To run git-review integration tests the following commands may by run::
tox -e py27
tox -e py26
tox -e py32
tox -e py33
depending on what Python interpreter would you like to use.

View File

@@ -1,22 +0,0 @@
============
git-review
============
``git-review`` is a tool that helps submitting git branches to gerrit
for review.
.. toctree::
:maxdepth: 2
installation
usage
developing
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View File

@@ -1,93 +0,0 @@
================================
Installation and Configuration
================================
Installing git-review
=====================
``git-review`` can be often be installed via system packages, ``pypi``
releases or other platform-specific methods. See
`<https://www.mediawiki.org/wiki/Gerrit/git-review>`__ for platform
information.
For assistance installing pacakges from ``pypi`` on your OS check out
`get-pip.py <https://pip.pypa.io/en/stable/installing/>`__.
For installation from source simply add ``git-review`` to your $PATH
after installing the dependencies listed in requirements.txt
Setup
=====
By default, git-review will look for a remote named 'gerrit' for working
with Gerrit. If the remote exists, git-review will submit the current
branch to HEAD:refs/for/master at that remote.
If the Gerrit remote does not exist, git-review looks for a file
called .gitreview at the root of the repository with information about
the gerrit remote. Assuming that file is present, git-review should
be able to automatically configure your repository the first time it
is run.
The name of the Gerrit remote is configurable; see the configuration
section below.
.gitreview file format
======================
Example .gitreview file (used to upload for git-review itself)::
[gerrit]
host=review.openstack.org
port=29418
project=openstack-infra/git-review.git
defaultbranch=master
Required values: host, project
Optional values: port (default: 29418), defaultbranch (default: master),
defaultremote (default: gerrit).
**Notes**
* Username is not required because it is requested on first run
* Unlike git config files, there cannot be any whitespace before the name
of the variable.
* Upon first run, git-review will create a remote for working with Gerrit,
if it does not already exist. By default, the remote name is 'gerrit',
but this can be overridden with the 'defaultremote' configuration
option.
* You can specify different values to be used as defaults in
~/.config/git-review/git-review.conf or /etc/git-review/git-review.conf.
* Git-review will query git credential system for gerrit user/password when
authentication failed over http(s). Unlike git, git-review does not persist
gerrit user/password in git credential system for security purposes and git
credential system configuration stays under user responsibility.
Hooks
=====
git-review has a custom hook mechanism to run a script before certain
actions. This is done in the same spirit as the classic hooks in git.
There are two types of hooks, a global one which is stored in
~/.config/git-review/hooks/ and one local to the repository stored in
.git/hooks/ with the other git hook scripts.
**The script needs be executable before getting executed**
The name of the script is $action-review where action can be
:
* pre - run at first before doing anything.
* post - run at the end after the review was sent.
* draft - run when in draft mode.
if the script returns with an exit status different than zero,
git-review will exit with the a custom shell exit code 71.

View File

@@ -1,55 +0,0 @@
=======
Usage
=======
Hack on some code, then::
git review
If you want to submit that code to a branch other than "master", then::
git review branchname
If you want to submit to a different remote::
git review -r my-remote
If you want to supply a review topic::
git review -t topic/awesome-feature
If you want to subscribe some reviewers::
git review --reviewers a@example.com b@example.com
If you want to disable autogenerated topic::
git review -T
If you want to submit a branch for review and then remove the local branch::
git review -f
If you want to skip the automatic "git rebase -i" step::
git review -R
If you want to download change 781 from gerrit to review it::
git review -d 781
If you want to download patchset 4 for change 781 from gerrit to review it::
git review -d 781,4
If you want to compare patchset 4 with patchset 10 of change 781 from gerrit::
git review -m 781,4-10
If you want to see a list of open reviews::
git review -l
If you just want to do the commit message and remote setup steps::
git review -s

View File

@@ -1,477 +0,0 @@
.\" Uses mdoc(7). See `man 7 mdoc` for details about the syntax used here
.\"
.Dd June 12th, 2015
.Dt GIT\-REVIEW 1
.Sh NAME
.Nm git\-review
.Nd Submit changes to Gerrit for review
.Sh SYNOPSIS
.Nm
.Op Fl r Ar remote
.Op Fl uv
.Fl d Ar change
.Op Ar branch
.Nm
.Op Fl r Ar remote
.Op Fl uv
.Fl x Ar change
.Op Ar branch
.Nm
.Op Fl r Ar remote
.Op Fl uv
.Fl N Ar change
.Op Ar branch
.Nm
.Op Fl r Ar remote
.Op Fl uv
.Fl X Ar change
.Op Ar branch
.Nm
.Op Fl r Ar remote
.Op Fl uv
.Fl m
.Ar change\-ps\-range
.Op Ar branch
.Nm
.Op Fl r Ar remote
.Op Fl fnuv
.Fl s
.Op Ar branch
.Nm
.Op Fl fnuvDRT
.Op Fl r Ar remote
.Op Fl t Ar topic
.Op Fl \-reviewers Ar reviewer ...
.Op Ar branch
.Nm
.Fl l
.Nm
.Fl \-version
.Sh DESCRIPTION
.Nm
automates and streamlines some of the tasks involved with
submitting local changes to a Gerrit server for review. It is
designed to make it easier to comprehend Gerrit, especially for
users that have recently switched to Git from another version
control system.
.Pp
.Ar change
can be
.Ar changeNumber
as obtained using
.Fl \-list
option, or it can be
.Ar changeNumber,patchsetNumber
for fetching exact patchset from the change.
In that case local branch name will have a \-patch[patchsetNumber] suffix.
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl c , Fl \-compatible
Push changes to refs compatible with Gerrit of versions before 2.3.
.It Fl d Ar change , Fl \-download= Ns Ar change
Download
.Ar change
from Gerrit
into a local branch. The branch will be named after the patch author and the name of a topic.
If the local branch already exists, it will attempt to update with the latest patchset for this change.
.It Fl x Ar change , Fl \-cherrypick= Ns Ar change
Apply
.Ar change
from Gerrit and commit into the current local branch ("cherry pick").
No additional branch is created.
.Pp
This makes it possible to review a change without creating a local branch for
it. On the other hand, be aware: if you are not careful, this can easily result
in additional patch sets for dependent changes. Also, if the current branch is
different enough, the change may not apply at all or produce merge conflicts
that need to be resolved by hand.
.It Fl N Ar change , Fl \-cherrypickonly= Ns Ar change
Apply
.Ar change
from Gerrit
into the current working directory, add it to the staging area ("git index"), but do not commit it.
.Pp
This makes it possible to review a change without creating a local commit for
it. Useful if you want to merge several commits into one that will be submitted for review.
.Pp
If the current branch is different enough, the change may not apply at all
or produce merge conflicts that need to be resolved by hand.
.It Fl X Ar change , Fl \-cherrypickindicate= Ns Ar change
Apply
.Ar change
from Gerrit and commit into the current local branch ("cherry pick"),
indicating which commit this change was cherry\-picked from.
.Pp
This makes it possible to re\-review a change for a different branch without
creating a local branch for it.
.Pp
If the current branch is different enough, the change may not apply at all
or produce merge conflicts that need to be resolved by hand.
.It Fl i , Fl \-new\-changeid
Force the git-review to generate a new Change-Id, even if one already exists
in the changelog.
.It Fl m Ar change\-ps\-range , Fl \-compare= Ns Ar change\-ps\-range
Download the specified patchsets for
.Ar change
from Gerrit, rebase both on master and display differences (git\-diff).
.Pp
.Ar change\-ps\-range
can be specified as
.Ar changeNumber, Ns Ar oldPatchSetNumber Ns Op Ns Ar \-newPatchSetNumber
.Pp
.Ar oldPatchSetNumber
is mandatory, and if
.Ar newPatchSetNumber
is not specified, the latest patchset will be used.
.Pp
This makes it possible to easily compare what has changed from last time you
reviewed the proposed change.
.Pp
If the master branch is different enough, the rebase can produce merge conflicts.
If that happens rebasing will be aborted and diff displayed for not\-rebased branches.
You can also use
.Ar \-\-no\-rebase ( Ar \-R )
to always skip rebasing.
.It Fl f , Fl \-finish
Close down the local branch and switch back to the target branch on
successful submission.
.It Fl F , Fl \-force\-rebase
Force a rebase before doing anything else, even if not otherwise needed.
.It Fl n , Fl \-dry\-run
Don\(aqt actually perform any commands that have direct effects. Print them
instead.
.It Fl r Ar remote , Fl \-remote= Ns Ar remote
Git remote to use for Gerrit.
.It Fl s , Fl \-setup
Just run the repo setup commands but don\(aqt submit anything.
.It Fl t Ar topic , Fl \-topic= Ns Ar topic
Sets the target topic for this change on the Gerrit server.
If not specified, a bug number from the commit summary will be used. Alternatively, the local branch name will be used if different from remote branch.
.It Fl T , Fl \-no\-topic
Submit review without topic.
.It Fl \-reviewers Ar reviewer ...
Subscribe one or more reviewers to the uploaded patch sets. Reviewers should be identifiable by Gerrit (usually use their Gerrit username or email address).
.It Fl u , Fl \-update
Skip cached local copies and force updates from network resources.
.It Fl l , Fl \-list
List the available reviews on the Gerrit server for this project.
.It Fl y , Fl \-yes
Indicate that you do, in fact, understand if you are submitting more than
one patch.
.It Fl v , Fl \-verbose
Turns on more verbose output.
.It Fl D , Fl \-draft
Submit review as a draft. Requires Gerrit 2.3 or newer.
.It Fl R , Fl \-no\-rebase
Do not automatically perform a rebase before submitting the change to
Gerrit.
.Pp
When submitting a change for review, you will usually want it to be based on the tip of upstream branch in order to avoid possible conflicts. When amending a change and rebasing the new patchset, the Gerrit web interface will show a difference between the two patchsets which contains all commits in between. This may confuse many reviewers that would expect to see a much simpler difference.
.Pp
Also can be used for
.Fl \-compare
to skip automatic rebase of fetched reviews.
.It Fl \-color Ar always|never|auto
Enable or disable a color output. Default is "auto".
.It Fl \-no\-color
Same thing as \-\-color=never.
.It Fl \-no\-custom\-script
Do not run scripts, installed as hooks/{action}-review, where action
is one of "pre", "draft", or "post".
.It Fl \-track
Choose the branch to submit the change against (and, if
rebasing, to rebase against) from the branch being tracked
(if a branch is being tracked), and set the tracking branch
when downloading a change to point to the remote and branch
against which patches should be submitted.
See gitreview.track configuration.
.It Fl \-no\-track
Ignore any branch being tracked by the current branch,
overriding gitreview.track.
This option is implied by providing a specific branch name
on the command line.
.It Fl \-use-pushurl
Use the pushurl option for the origin remote rather than conventional
separate Gerrit remotes.
.It Fl \-version
Print the version number and exit.
.El
.Sh CONFIGURATION
This utility can be configured by adding entries to Git configuration.
.Pp
The following configuration keys are supported:
.Bl -tag
.It gitreview.username
Default username used to access the repository. If not specified
in the Git configuration, Git remote or
.Pa .gitreview
file, the user will be prompted to specify the username.
.Pp
Example entry in the
.Pa .gitconfig
file:
.Bd -literal -offset indent
[gitreview]
username=\fImygerrituser\fP
.Ed
.It gitreview.scheme
This setting determines the default scheme (ssh/http/https) of gerrit remote
.It gitreview.host
This setting determines the default hostname of gerrit remote
.It gitreview.port
This setting determines the default port of gerrit remote
.It gitreview.project
This setting determines the default name of gerrit git repo
.It gitreview.remote
This setting determines the default name to use for gerrit remote
.It gitreview.branch
This setting determines the default branch
.It gitreview.track
Determines whether to prefer the currently-tracked branch (if any)
and the branch against which the changeset was submitted to Gerrit
(if there is exactly one such branch) to the defaultremote and
defaultbranch for submitting and rebasing against.
If the local topic branch is tracking a remote branch, the remote
and branch that the local topic branch is tracking should be used
for submit and rebase operations, rather than the defaultremote
and defaultbranch.
.Pp
When downloading a patch, creates the local branch to track the
appropriate remote and branch in order to choose that branch by
default when submitting modifications to that changeset.
.Pp
A value of 'true' or 'false' should be specified.
.Bl -tag
.It true
Do prefer the currently-tracked branch (if any) \- equivalent
to setting
.Fl \-track
when submitting changes.
.It false
Ignore tracking branches \- equivalent to setting
.Fl \-no\-track
(the default) or providing an explicit branch name when submitting
changes. This is the default value unless overridden by
.Pa .gitreview
file, and is implied by providing a specific branch name on the
command line.
.El
.It gitreview.rebase
This setting determines whether changes submitted will
be rebased to the newest state of the branch.
.Pp
A value of 'true' or 'false' should be specified.
.Bl -tag
.It false
Do not rebase changes on submit \- equivalent to setting
.Fl R
when submitting changes.
.It true
Do rebase changes on submit. This is the default value unless
overridden by
.Pa .gitreview
file.
.El
.Pp
This setting takes precedence over repository\-specific configuration
in the
.Pa .gitreview
file.
.El
.Bl -tag
.It color.review
Whether to use ANSI escape sequences to add color to the output displayed by
this command. Default value is determined by color.ui.
.Bl -tag
.It auto or true
If you want output to use color when written to the terminal (default with Git
1.8.4 and newer).
.It always
If you want all output to use color
.It never or false
If you wish not to use color for any output. (default with Git older than 1.8.4)
.El
.El
.Pp
.Nm
will query git credential system for Gerrit user/password when
authentication failed over http(s). Unlike git,
.Nm
does not persist Gerrit user/password in git credential system for security
purposes and git credential system configuration stays under user responsibility.
.Sh FILES
To use
.Nm
with your project, it is recommended that you create
a file at the root of the repository named
.Pa .gitreview
and place information about your Gerrit installation in it. The format is similar to the Windows .ini file format:
.Bd -literal -offset indent
[gerrit]
host=\fIhostname\fP
port=\fITCP port number of gerrit\fP
project=\fIproject name\fP
defaultbranch=\fIbranch to work on\fP
.Ed
.Pp
It is also possible to specify optional default name for
the Git remote using the
.Cm defaultremote
configuration parameter.
.Pp
Setting
.Cm defaultrebase
to zero will make
.Nm
not to rebase changes by default (same as the
.Fl R
command line option)
.Bd -literal -offset indent
[gerrit]
scheme=ssh
host=review.example.com
port=29418
project=department/project.git
defaultbranch=master
defaultremote=review
defaultrebase=0
track=0
.Ed
.Pp
When the same option is provided through FILES and CONFIGURATION, the
CONFIGURATION value wins.
.Pp
.Sh DIAGNOSTICS
.Pp
Normally, exit status is 0 if executed successfully.
Exit status 1 indicates general error, sometimes more
specific error codes are available:
.Bl -tag -width 999
.It 2
Gerrit
.Ar commit\-msg
hook could not be successfully installed.
.It 3
Could not parse malformed argument value or user input.
.It 32
Cannot fetch list of open changesets from Gerrit.
.It 33
Cannot parse list of open changesets received from Gerrit.
.It 34
Cannot query information about changesets.
.It 35
Cannot fetch information about the changeset to be downloaded.
.It 36
Changeset not found.
.It 37
Particular patchset cannot be fetched from the remote git repository.
.It 38
Specified patchset number not found in the changeset.
.It 39
Invalid patchsets for comparison.
.It 40
Connection to Gerrit was closed.
.It 64
Cannot checkout downloaded patchset into the new branch.
.It 65
Cannot checkout downloaded patchset into existing branch.
.It 66
Cannot hard reset working directory and git index after download.
.It 67
Cannot switch to some other branch when trying to finish
the current branch.
.It 68
Cannot delete current branch.
.It 69
Requested patchset cannot be fully applied to the current branch. This exit
status will be returned when there are merge conflicts with the current branch.
Possible reasons include an attempt to apply patchset from the different branch
or code. This exit status will also be returned if the patchset is already
applied to the current branch.
.It 70
Cannot determine top level Git directory or .git subdirectory path.
.It 101
Unauthorized (401) http request done by git-review.
.It 104
Not Found (404) http request done by git-review.
.El
.Pp
Exit status larger than 31 indicates problem with
communication with Gerrit or remote Git repository,
exit status larger than 63 means there was a problem with
a local repository or a working copy.
.Pp
Exit status larger than or equal to 128 means internal
error in running the "git" command.
.Pp
.Sh EXAMPLES
To fetch a remote change number 3004:
.Pp
.Bd -literal -offset indent
$ git\-review \-d 3004
Downloading refs/changes/04/3004/1 from gerrit into
review/someone/topic_name
Switched to branch 'review/someone/topic_name
$ git branch
master
* review/author/topic_name
.Ed
.Pp
Gerrit looks up both name of the author and the topic name from Gerrit
to name a local branch. This facilitates easier identification of changes.
.Pp
To fetch a remote patchset number 5 from change number 3004:
.Pp
.Bd -literal -offset indent
$ git\-review \-d 3004,5
Downloading refs/changes/04/3004/5 from gerrit into
review/someone/topic_name\-patch5
Switched to branch 'review/someone/topic_name\-patch5
$ git branch
master
* review/author/topic_name\-patch5
.Ed
.Pp
To send a change for review and delete local branch afterwards:
.Bd -literal -offset indent
$ git\-review \-f
remote: Resolving deltas: 0% (0/8)
To ssh://username@review.example.com/department/project.git
* [new branch] HEAD \-> refs/for/master/topic_name
Switched to branch 'master'
Deleted branch 'review/someone/topic_name'
$ git branch
* master
.Ed
.Pp
An example
.Pa .gitreview
configuration file for a project
.Pa department/project
hosted on
.Cm review.example.com
port
.Cm 29418
in the branch
.Cm master
:
.Bd -literal -offset indent
[gerrit]
host=review.example.com
port=29418
project=department/project.git
defaultbranch=master
.Ed
.Sh BUGS
Bug reports can be submitted to
.Lk https://launchpad.net/git\-review
.Sh AUTHORS
.Nm
is maintained by
.An "OpenStack, LLC"
.Pp
This manpage has been enhanced by:
.An "Antoine Musso" Aq hashar@free.fr
.An "Marcin Cieslak" Aq saper@saper.info
.An "Pavel Sedlák" Aq psedlak@redhat.com

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,408 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 hashlib
import os
import shutil
import stat
import struct
import sys
if sys.version < '3':
import urllib
import urlparse
urlparse = urlparse.urlparse
else:
import urllib.parse
import urllib.request
urlparse = urllib.parse.urlparse
import fixtures
import requests
import testtools
from testtools import content
from git_review.tests import utils
WAR_URL = 'http://tarballs.openstack.org/' \
'ci/gerrit/gerrit-v2.11.4.13.cb9800e.war'
# Update GOLDEN_SITE_VER for every change altering golden site, including
# WAR_URL changes. Set new value to something unique (just +1 it for example)
GOLDEN_SITE_VER = '4'
# NOTE(yorik-sar): This function needs to be a perfect hash function for
# existing test IDs. This is verified by running check_test_id_hashes script
# prior to running tests. Note that this doesn't imply any cryptographic
# requirements for underlying algorithm, so we can use weak hash here.
# Range of results for this function is limited by port numbers selection
# in _pick_gerrit_port_and_dir method (it can't be greater than 10000 now).
def _hash_test_id(test_id):
if not isinstance(test_id, bytes):
test_id = test_id.encode('utf-8')
hash_ = hashlib.md5(test_id).digest()
num = struct.unpack("=I", hash_[:4])[0]
return num % 10000
class DirHelpers(object):
def _dir(self, base, *args):
"""Creates directory name from base name and other parameters."""
return os.path.join(getattr(self, base + '_dir'), *args)
class IsoEnvDir(DirHelpers, fixtures.Fixture):
"""Created isolated env and associated directories"""
def __init__(self, base_dir=None):
super(IsoEnvDir, self).setUp()
# set up directories for isolation
if base_dir:
self.base_dir = base_dir
self.temp_dir = self._dir('base', 'tmp')
if not os.path.exists(self.temp_dir):
os.mkdir(self.temp_dir)
self.addCleanup(shutil.rmtree, self.temp_dir)
else:
self.temp_dir = self.useFixture(fixtures.TempDir()).path
self.work_dir = self._dir('temp', 'work')
self.home_dir = self._dir('temp', 'home')
self.xdg_config_dir = self._dir('home', '.xdgconfig')
for path in [self.home_dir, self.xdg_config_dir, self.work_dir]:
if not os.path.exists(path):
os.mkdir(path)
# ensure user proxy conf doesn't interfere with tests
self.useFixture(fixtures.EnvironmentVariable('no_proxy', '*'))
self.useFixture(fixtures.EnvironmentVariable('NO_PROXY', '*'))
# isolate tests from user and system git configuration
self.useFixture(fixtures.EnvironmentVariable('HOME', self.home_dir))
self.useFixture(
fixtures.EnvironmentVariable('XDG_CONFIG_HOME',
self.xdg_config_dir))
self.useFixture(
fixtures.EnvironmentVariable('GIT_CONFIG_NOSYSTEM', "1"))
self.useFixture(
fixtures.EnvironmentVariable('EMAIL', "you@example.com"))
self.useFixture(
fixtures.EnvironmentVariable('GIT_AUTHOR_NAME',
"gitreview tester"))
self.useFixture(
fixtures.EnvironmentVariable('GIT_COMMITTER_NAME',
"gitreview tester"))
class GerritHelpers(DirHelpers):
def init_dirs(self):
self.primary_dir = os.path.abspath(os.path.curdir)
self.gerrit_dir = self._dir('primary', '.gerrit')
self.gsite_dir = self._dir('gerrit', 'golden_site')
self.gerrit_war = self._dir('gerrit', WAR_URL.split('/')[-1])
def ensure_gerrit_war(self):
# check if gerrit.war file exists in .gerrit directory
if not os.path.exists(self.gerrit_dir):
os.mkdir(self.gerrit_dir)
if not os.path.exists(self.gerrit_war):
print("Downloading Gerrit binary from %s..." % WAR_URL)
resp = requests.get(WAR_URL)
if resp.status_code != 200:
raise RuntimeError("Problem requesting Gerrit war")
utils.write_to_file(self.gerrit_war, resp.content)
print("Saved to %s" % self.gerrit_war)
def init_gerrit(self):
"""Run Gerrit from the war file and configure it."""
golden_ver_file = self._dir('gsite', 'golden_ver')
if os.path.exists(self.gsite_dir):
if not os.path.exists(golden_ver_file):
golden_ver = '0'
else:
with open(golden_ver_file) as f:
golden_ver = f.read().strip()
if GOLDEN_SITE_VER != golden_ver:
print("Existing golden site has version %s, removing..." %
golden_ver)
shutil.rmtree(self.gsite_dir)
else:
print("Golden site of version %s already exists" %
GOLDEN_SITE_VER)
return
# We write out the ssh host key for gerrit's ssh server which
# for undocumented reasons forces gerrit init to download the
# bouncy castle libs which we need for ssh that works on
# newer distros like ubuntu xenial.
os.makedirs(self._dir('gsite', 'etc'))
# create SSH host key
host_key_file = self._dir('gsite', 'etc', 'ssh_host_rsa_key')
utils.run_cmd('ssh-keygen', '-t', 'rsa', '-b', '4096',
'-f', host_key_file, '-N', '')
print("Creating a new golden site of version " + GOLDEN_SITE_VER)
# initialize Gerrit
utils.run_cmd('java', '-jar', self.gerrit_war,
'init', '-d', self.gsite_dir,
'--batch', '--no-auto-start', '--install-plugin',
'download-commands')
utils.run_cmd('java', '-jar', self.gerrit_war, 'reindex',
'-d', self.gsite_dir)
with open(golden_ver_file, 'w') as f:
f.write(GOLDEN_SITE_VER)
# create SSH public key
key_file = self._dir('gsite', 'test_ssh_key')
utils.run_cmd('ssh-keygen', '-t', 'rsa', '-b', '4096',
'-f', key_file, '-N', '')
with open(key_file + '.pub', 'rb') as pub_key_file:
pub_key = pub_key_file.read()
# create admin user in Gerrit database
sql_query = """INSERT INTO ACCOUNTS (REGISTERED_ON) VALUES (NOW());
INSERT INTO ACCOUNT_GROUP_MEMBERS (ACCOUNT_ID, GROUP_ID) \
VALUES (0, 1);
INSERT INTO ACCOUNT_EXTERNAL_IDS (ACCOUNT_ID, EXTERNAL_ID, PASSWORD) \
VALUES (0, 'username:test_user', 'test_pass');
INSERT INTO ACCOUNT_SSH_KEYS (SSH_PUBLIC_KEY, VALID) \
VALUES ('%s', 'Y')""" % pub_key.decode()
utils.run_cmd('java', '-jar',
self._dir('gsite', 'bin', 'gerrit.war'),
'gsql', '-d', self.gsite_dir, '-c', sql_query)
def _run_gerrit_cli(self, command, *args):
"""SSH to gerrit Gerrit server and run command there."""
return utils.run_cmd('ssh', '-p', str(self.gerrit_port),
'test_user@' + self.gerrit_host, 'gerrit',
command, *args)
def _run_git_review(self, *args, **kwargs):
"""Run git-review utility from source."""
git_review = utils.run_cmd('which', 'git-review')
kwargs.setdefault('chdir', self.test_dir)
return utils.run_cmd(git_review, *args, **kwargs)
class BaseGitReviewTestCase(testtools.TestCase, GerritHelpers):
"""Base class for the git-review tests."""
_remote = 'gerrit'
@property
def project_uri(self):
return self.project_ssh_uri
def setUp(self):
"""Configure testing environment.
Prepare directory for the testing and clone test Git repository.
Require Gerrit war file in the .gerrit directory to run Gerrit local.
"""
super(BaseGitReviewTestCase, self).setUp()
self.useFixture(fixtures.Timeout(2 * 60, True))
# ensures git-review command runs in local mode (for functional tests)
self.useFixture(
fixtures.EnvironmentVariable('GITREVIEW_LOCAL_MODE', ''))
self.init_dirs()
ssh_addr, ssh_port, http_addr, http_port, self.site_dir = \
self._pick_gerrit_port_and_dir()
self.gerrit_host, self.gerrit_port = ssh_addr, ssh_port
self.test_dir = self._dir('site', 'tmp', 'test_project')
self.ssh_dir = self._dir('site', 'tmp', 'ssh')
self.project_ssh_uri = (
'ssh://test_user@%s:%s/test/test_project.git' % (
ssh_addr, ssh_port))
self.project_http_uri = (
'http://test_user:test_pass@%s:%s/test/test_project.git' % (
http_addr, http_port))
self._run_gerrit(ssh_addr, ssh_port, http_addr, http_port)
self._configure_ssh(ssh_addr, ssh_port)
# create Gerrit empty project
self._run_gerrit_cli('create-project', '--empty-commit',
'--name', 'test/test_project')
# setup isolated area to work under
self.useFixture(IsoEnvDir(self.site_dir))
# prepare repository for the testing
self._run_git('clone', self.project_uri)
utils.write_to_file(self._dir('test', 'test_file.txt'),
'test file created'.encode())
self._create_gitreview_file()
# push changes to the Gerrit
self._run_git('add', '--all')
self._run_git('commit', '-m', 'Test file and .gitreview added.')
self._run_git('push', 'origin', 'master')
# push a branch to gerrit
self._run_git('checkout', '-b', 'testbranch')
utils.write_to_file(self._dir('test', 'test_file.txt'),
'test file branched'.encode())
self._create_gitreview_file(defaultbranch='testbranch')
self._run_git('add', '--all')
self._run_git('commit', '-m', 'Branched.')
self._run_git('push', 'origin', 'testbranch')
# cleanup
shutil.rmtree(self.test_dir)
# go to the just cloned test Git repository
self._run_git('clone', self.project_uri)
self.configure_gerrit_remote()
self.addCleanup(shutil.rmtree, self.test_dir)
# ensure user is configured for all tests
self._configure_gitreview_username()
def set_remote(self, uri):
self._run_git('remote', 'set-url', self._remote, uri)
def reset_remote(self):
self._run_git('remote', 'rm', self._remote)
def attach_on_exception(self, filename):
@self.addOnException
def attach_file(exc_info):
if os.path.exists(filename):
content.attach_file(self, filename)
else:
self.addDetail(os.path.basename(filename),
content.text_content('Not found'))
def _run_git(self, command, *args):
"""Run git command using test git directory."""
if command == 'clone':
return utils.run_git(command, args[0], self._dir('test'))
return utils.run_git('--git-dir=' + self._dir('test', '.git'),
'--work-tree=' + self._dir('test'),
command, *args)
def _run_gerrit(self, ssh_addr, ssh_port, http_addr, http_port):
# create a copy of site dir
shutil.copytree(self.gsite_dir, self.site_dir)
self.addCleanup(shutil.rmtree, self.site_dir)
# write config
with open(self._dir('site', 'etc', 'gerrit.config'), 'w') as _conf:
new_conf = utils.get_gerrit_conf(
ssh_addr, ssh_port, http_addr, http_port)
_conf.write(new_conf)
# If test fails, attach Gerrit config and logs to the result
self.attach_on_exception(self._dir('site', 'etc', 'gerrit.config'))
for name in ['error_log', 'sshd_log', 'httpd_log']:
self.attach_on_exception(self._dir('site', 'logs', name))
# start Gerrit
gerrit_sh = self._dir('site', 'bin', 'gerrit.sh')
utils.run_cmd(gerrit_sh, 'start')
self.addCleanup(utils.run_cmd, gerrit_sh, 'stop')
def _simple_change(self, change_text, commit_message,
file_=None):
"""Helper method to create small changes and commit them."""
if file_ is None:
file_ = self._dir('test', 'test_file.txt')
utils.write_to_file(file_, change_text.encode())
self._run_git('add', file_)
self._run_git('commit', '-m', commit_message)
def _simple_amend(self, change_text, file_=None):
"""Helper method to amend existing commit with change."""
if file_ is None:
file_ = self._dir('test', 'test_file_new.txt')
utils.write_to_file(file_, change_text.encode())
self._run_git('add', file_)
# cannot use --no-edit because it does not exist in older git
message = self._run_git('log', '-1', '--format=%s\n\n%b')
self._run_git('commit', '--amend', '-m', message)
def _configure_ssh(self, ssh_addr, ssh_port):
"""Setup ssh and scp to run with special options."""
os.mkdir(self.ssh_dir)
ssh_key = utils.run_cmd('ssh-keyscan', '-p', str(ssh_port), ssh_addr)
utils.write_to_file(self._dir('ssh', 'known_hosts'), ssh_key.encode())
self.addCleanup(os.remove, self._dir('ssh', 'known_hosts'))
# Attach known_hosts to test results if anything fails
self.attach_on_exception(self._dir('ssh', 'known_hosts'))
for cmd in ('ssh', 'scp'):
cmd_file = self._dir('ssh', cmd)
s = '#!/bin/sh\n' \
'/usr/bin/%s -i %s -o UserKnownHostsFile=%s ' \
'-o IdentitiesOnly=yes ' \
'-o PasswordAuthentication=no $@' % \
(cmd,
self._dir('gsite', 'test_ssh_key'),
self._dir('ssh', 'known_hosts'))
utils.write_to_file(cmd_file, s.encode())
os.chmod(cmd_file, os.stat(cmd_file).st_mode | stat.S_IEXEC)
os.environ['PATH'] = self.ssh_dir + os.pathsep + os.environ['PATH']
os.environ['GIT_SSH'] = self._dir('ssh', 'ssh')
def configure_gerrit_remote(self):
self._run_git('remote', 'add', self._remote, self.project_uri)
def _configure_gitreview_username(self):
self._run_git('config', 'gitreview.username', 'test_user')
def _pick_gerrit_port_and_dir(self):
hash_ = _hash_test_id(self.id())
host = '127.0.0.1'
return (
host, 12000 + hash_, # avoid 11211 that is memcached port on CI
host, 22000 + hash_, # avoid ephemeral ports at 32678+
self._dir('gerrit', 'site-' + str(hash_)),
)
def _create_gitreview_file(self, **kwargs):
cfg = ('[gerrit]\n'
'scheme=%s\n'
'host=%s\n'
'port=%s\n'
'project=test/test_project.git\n'
'%s')
parsed = urlparse(self.project_uri)
host_port = parsed.netloc.rpartition('@')[-1]
host, __, port = host_port.partition(':')
extra = '\n'.join('%s=%s' % kv for kv in kwargs.items())
cfg %= parsed.scheme, host, port, extra
utils.write_to_file(self._dir('test', '.gitreview'), cfg.encode())
class HttpMixin(object):
"""HTTP remote_url mixin."""
@property
def project_uri(self):
return self.project_http_uri
def _configure_gitreview_username(self):
# trick to set http password
self._run_git('config', 'gitreview.username', 'test_user:test_pass')

View File

@@ -1,57 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 sys
from git_review import tests
from git_review.tests import utils
def list_test_ids(argv):
res = utils.run_cmd(sys.executable, '-m', 'testtools.run', *argv[1:])
return res.split('\n')
def find_collisions(test_ids):
hashes = {}
for test_id in test_ids:
hash_ = tests._hash_test_id(test_id)
if hash_ in hashes:
return (hashes[hash_], test_id)
hashes[hash_] = test_id
return None
def main(argv):
test_ids = list_test_ids(argv)
if not test_ids:
print("No tests found, check command line arguments", file=sys.stderr)
return 1
collision = find_collisions(test_ids)
if collision is None:
return 0
print(
"Found a collision for test ids hash function: %s and %s\n"
"You should change _hash_test_id function in"
" git_review/tests/__init__.py module to fit new set of test ids."
% collision,
file=sys.stderr,
)
return 2
if __name__ == "__main__":
sys.exit(main(sys.argv))

View File

@@ -1,26 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 git_review import tests
def main():
helpers = tests.GerritHelpers()
helpers.init_dirs()
helpers.ensure_gerrit_war()
helpers.init_gerrit()
if __name__ == "__main__":
main()

View File

@@ -1,567 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2013 Mirantis Inc.
#
# 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 json
import os
import shutil
from git_review import tests
from git_review.tests import utils
class ConfigTestCase(tests.BaseGitReviewTestCase):
"""Class for config tests."""
def test_get_config_from_cli(self):
self.reset_remote()
self._run_git('remote', 'rm', 'origin')
self._create_gitreview_file(defaultremote='remote-file')
self._run_git('config', 'gitreview.remote', 'remote-gitconfig')
self._run_git_review('-s', '-r', 'remote-cli')
remote = self._run_git('remote').strip()
self.assertEqual('remote-cli', remote)
def test_get_config_from_gitconfig(self):
self.reset_remote()
self._run_git('remote', 'rm', 'origin')
self._create_gitreview_file(defaultremote='remote-file')
self._run_git('config', 'gitreview.remote', 'remote-gitconfig')
self._run_git_review('-s')
remote = self._run_git('remote').strip()
self.assertEqual('remote-gitconfig', remote)
def test_get_config_from_file(self):
self.reset_remote()
self._run_git('remote', 'rm', 'origin')
self._create_gitreview_file(defaultremote='remote-file')
self._run_git_review('-s')
remote = self._run_git('remote').strip()
self.assertEqual('remote-file', remote)
class GitReviewTestCase(tests.BaseGitReviewTestCase):
"""Class for the git-review tests."""
def test_cloned_repo(self):
"""Test git-review on the just cloned repository."""
self._simple_change('test file modified', 'test commit message')
self.assertNotIn('Change-Id:', self._run_git('log', '-1'))
self.assertIn('remote: New Changes:', self._run_git_review())
self.assertIn('Change-Id:', self._run_git('log', '-1'))
def test_git_review_s(self):
"""Test git-review -s."""
self.reset_remote()
self._run_git_review('-s')
self._simple_change('test file modified', 'test commit message')
self.assertIn('Change-Id:', self._run_git('log', '-1'))
def test_git_review_s_in_detached_head(self):
"""Test git-review -s in detached HEAD state."""
self.reset_remote()
master_sha1 = self._run_git('rev-parse', 'master')
self._run_git('checkout', master_sha1)
self._run_git_review('-s')
self._simple_change('test file modified', 'test commit message')
self.assertIn('Change-Id:', self._run_git('log', '-1'))
def test_git_review_s_with_outdated_repo(self):
"""Test git-review -s with a outdated repo."""
self._simple_change('test file to outdate', 'test commit message 1')
self._run_git('push', 'origin', 'master')
self._run_git('reset', '--hard', 'HEAD^')
# Review setup with an outdated repo
self.reset_remote()
self._run_git_review('-s')
self._simple_change('test file modified', 'test commit message 2')
self.assertIn('Change-Id:', self._run_git('log', '-1'))
def test_git_review_s_from_subdirectory(self):
"""Test git-review -s from subdirectory."""
self.reset_remote()
utils.run_cmd('mkdir', 'subdirectory', chdir=self.test_dir)
self._run_git_review(
'-s', chdir=os.path.join(self.test_dir, 'subdirectory'))
def test_git_review_d(self):
"""Test git-review -d."""
self._run_git_review('-s')
# create new review to be downloaded
self._simple_change('test file modified', 'test commit message')
self._run_git_review()
change_id = self._run_git('log', '-1').split()[-1]
shutil.rmtree(self.test_dir)
# download clean Git repository and fresh change from Gerrit to it
self._run_git('clone', self.project_uri)
self.configure_gerrit_remote()
self._run_git_review('-d', change_id)
self.assertIn('test commit message', self._run_git('log', '-1'))
# test backport branch
self._run_git('checkout', '-b', 'mybackport',
self._remote + '/' + 'testbranch')
self._simple_change('test file modified in branch',
'test branch commit message\n\nChange-Id: %s' %
change_id)
self._run_git_review('testbranch')
self._run_git('checkout', 'master')
self._run_git_review('-d', change_id, 'testbranch')
self.assertIn('test branch commit message',
self._run_git('log', '-1'))
# second download should also work correctly
self._run_git('checkout', 'master')
self._run_git_review('-d', change_id)
self.assertIn('test commit message', self._run_git('show', 'HEAD'))
self.assertNotIn('test commit message',
self._run_git('show', 'HEAD^1'))
# and branch is tracking
head = self._run_git('symbolic-ref', '-q', 'HEAD')
self.assertIn(
'refs/remotes/%s/master' % self._remote,
self._run_git("for-each-ref", "--format='%(upstream)'", head))
def test_multiple_changes(self):
"""Test git-review asks about multiple changes.
Should register user's wish to send two change requests by interactive
'yes' message and by the -y option.
"""
self._run_git_review('-s')
# 'yes' message
self._simple_change('test file modified 1st time',
'test commit message 1')
self._simple_change('test file modified 2nd time',
'test commit message 2')
review_res = self._run_git_review(confirm=True)
self.assertIn("Type 'yes' to confirm", review_res)
self.assertIn("Processing changes: new: 2", review_res)
# abandon changes sent to the Gerrit
head = self._run_git('rev-parse', 'HEAD')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_gerrit_cli('review', '--abandon', head)
self._run_gerrit_cli('review', '--abandon', head_1)
# -y option
self._simple_change('test file modified 3rd time',
'test commit message 3')
self._simple_change('test file modified 4th time',
'test commit message 4')
review_res = self._run_git_review('-y')
self.assertIn("Processing changes: new: 2", review_res)
def test_git_review_re(self):
"""Test git-review adding reviewers to changes."""
self._run_git_review('-s')
# Create users to add as reviewers
self._run_gerrit_cli('create-account', '--email',
'reviewer1@example.com', 'reviewer1')
self._run_gerrit_cli('create-account', '--email',
'reviewer2@example.com', 'reviewer2')
self._simple_change('test file', 'test commit message')
review_res = self._run_git_review('--reviewers', 'reviewer1',
'reviewer2')
self.assertIn("Processing changes: new: 1", review_res)
# verify both reviewers are on patch set
head = self._run_git('rev-parse', 'HEAD')
change = self._run_gerrit_cli('query', '--format=JSON',
'--all-reviewers', head)
# The first result should be the one we want
change = json.loads(change.split('\n')[0])
self.assertEqual(2, len(change['allReviewers']))
reviewers = set()
for reviewer in change['allReviewers']:
reviewers.add(reviewer['username'])
self.assertEqual(set(['reviewer1', 'reviewer2']), reviewers)
def test_rebase_no_remote_branch_msg(self):
"""Test message displayed where no remote branch exists."""
self._run_git_review('-s')
self._run_git('checkout', '-b', 'new_branch')
self._simple_change('simple message',
'need to avoid noop message')
exc = self.assertRaises(Exception, self._run_git_review, 'new_branch')
self.assertIn("The branch 'new_branch' does not exist on the given "
"remote '%s'" % self._remote, exc.args[0])
def test_need_rebase_no_upload(self):
"""Test change needing a rebase does not upload."""
self._run_git_review('-s')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_git('checkout', '-b', 'test_branch', head_1)
self._simple_change('some other message',
'create conflict with master')
exc = self.assertRaises(Exception, self._run_git_review)
self.assertIn(
"Errors running git rebase -p -i remotes/%s/master" % self._remote,
exc.args[0])
self.assertIn("It is likely that your change has a merge conflict.",
exc.args[0])
def test_upload_without_rebase(self):
"""Test change not needing a rebase can upload without rebasing."""
self._run_git_review('-s')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_git('checkout', '-b', 'test_branch', head_1)
self._simple_change('some new message',
'just another file (no conflict)',
self._dir('test', 'new_test_file.txt'))
review_res = self._run_git_review('-v')
self.assertIn(
"Running: git rebase -p -i remotes/%s/master" % self._remote,
review_res)
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head_1)
def test_uploads_with_nondefault_rebase(self):
"""Test changes rebase against correct branches."""
# prepare maintenance branch that is behind master
self._create_gitreview_file(track='true',
defaultremote='origin')
self._run_git('add', '.gitreview')
self._run_git('commit', '-m', 'track=true.')
self._simple_change('diverge master from maint',
'no conflict',
self._dir('test', 'test_file_to_diverge.txt'))
self._run_git('push', 'origin', 'master')
self._run_git('push', 'origin', 'master', 'master:other')
self._run_git_review('-s')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_gerrit_cli('create-branch',
'test/test_project',
'maint', head_1)
self._run_git('fetch')
br_out = self._run_git('checkout',
'-b', 'test_branch', 'origin/maint')
expected_track = 'Branch test_branch set up to track remote' + \
' branch maint from origin.'
self.assertIn(expected_track, br_out)
branches = self._run_git('branch', '-a')
expected_branch = '* test_branch'
observed = branches.split('\n')
self.assertIn(expected_branch, observed)
self._simple_change('some new message',
'just another file (no conflict)',
self._dir('test', 'new_tracked_test_file.txt'))
change_id = self._run_git('log', '-1').split()[-1]
review_res = self._run_git_review('-v')
# no rebase needed; if it breaks it would try to rebase to master
self.assertNotIn("Running: git rebase -p -i remotes/origin/master",
review_res)
# Don't need to query gerrit for the branch as the second half
# of this test will work only if the branch was correctly
# stored in gerrit
# delete branch locally
self._run_git('checkout', 'master')
self._run_git('branch', '-D', 'test_branch')
# download, amend, submit
self._run_git_review('-d', change_id)
self._simple_amend('just another file (no conflict)',
self._dir('test', 'new_tracked_test_file_2.txt'))
new_change_id = self._run_git('log', '-1').split()[-1]
self.assertEqual(change_id, new_change_id)
review_res = self._run_git_review('-v')
# caused the right thing to happen
self.assertIn("Running: git rebase -p -i remotes/origin/maint",
review_res)
# track different branch than expected in changeset
branch = self._run_git('rev-parse', '--abbrev-ref', 'HEAD')
self._run_git('branch',
'--set-upstream',
branch,
'remotes/origin/other')
self.assertRaises(
Exception, # cmd.BranchTrackingMismatch inside
self._run_git_review, '-d', change_id)
def test_no_rebase_check(self):
"""Test -R causes a change to be uploaded without rebase checking."""
self._run_git_review('-s')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_git('checkout', '-b', 'test_branch', head_1)
self._simple_change('some new message', 'just another file',
self._dir('test', 'new_test_file.txt'))
review_res = self._run_git_review('-v', '-R')
self.assertNotIn('rebase', review_res)
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head_1)
def test_rebase_anyway(self):
"""Test -F causes a change to be rebased regardless."""
self._run_git_review('-s')
head = self._run_git('rev-parse', 'HEAD')
head_1 = self._run_git('rev-parse', 'HEAD^1')
self._run_git('checkout', '-b', 'test_branch', head_1)
self._simple_change('some new message', 'just another file',
self._dir('test', 'new_test_file.txt'))
review_res = self._run_git_review('-v', '-F')
self.assertIn('rebase', review_res)
self.assertEqual(self._run_git('rev-parse', 'HEAD^1'), head)
def _assert_branch_would_be(self, branch, extra_args=None):
extra_args = extra_args or []
output = self._run_git_review('-n', *extra_args)
# last non-empty line should be:
# git push gerrit HEAD:refs/publish/master
last_line = output.strip().split('\n')[-1]
branch_was = last_line.rsplit(' ', 1)[-1].split('/', 2)[-1]
self.assertEqual(branch, branch_was)
def test_detached_head(self):
"""Test on a detached state: we shouldn't have '(detached' as topic."""
self._run_git_review('-s')
curr_branch = self._run_git('rev-parse', '--abbrev-ref', 'HEAD')
# Note: git checkout --detach has been introduced in git 1.7.5 (2011)
self._run_git('checkout', curr_branch + '^0')
self._simple_change('some new message', 'just another file',
self._dir('test', 'new_test_file.txt'))
# switch to French, 'git branch' should return '(détaché du HEAD)'
lang_env = os.getenv('LANG', 'C')
os.environ.update(LANG='fr_FR.UTF-8')
try:
self._assert_branch_would_be(curr_branch)
finally:
os.environ.update(LANG=lang_env)
def test_git_review_t(self):
self._run_git_review('-s')
self._simple_change('test file modified', 'commit message for bug 654')
self._assert_branch_would_be('master/zat', extra_args=['-t', 'zat'])
def test_bug_topic(self):
self._run_git_review('-s')
self._simple_change('a change', 'new change for bug 123')
self._assert_branch_would_be('master/bug/123')
def test_bug_topic_newline(self):
self._run_git_review('-s')
self._simple_change('a change', 'new change not for bug\n\n123')
self._assert_branch_would_be('master')
def test_bp_topic(self):
self._run_git_review('-s')
self._simple_change('a change', 'new change for blueprint asdf')
self._assert_branch_would_be('master/bp/asdf')
def test_bp_topic_newline(self):
self._run_git_review('-s')
self._simple_change('a change', 'new change not for blueprint\n\nasdf')
self._assert_branch_would_be('master')
def test_author_name_topic_bp(self):
old_author = None
if 'GIT_AUTHOR_NAME' in os.environ:
old_author = os.environ['GIT_AUTHOR_NAME']
try:
os.environ['GIT_AUTHOR_NAME'] = 'BPNAME'
self._run_git_review('-s')
self._simple_change('a change',
'new change 1 with name but no topic')
self._assert_branch_would_be('master')
finally:
if old_author:
os.environ['GIT_AUTHOR_NAME'] = old_author
else:
del os.environ['GIT_AUTHOR_NAME']
def test_author_email_topic_bp(self):
old_author = None
if 'GIT_AUTHOR_EMAIL' in os.environ:
old_author = os.environ['GIT_AUTHOR_EMAIL']
try:
os.environ['GIT_AUTHOR_EMAIL'] = 'bpemail@example.com'
self._run_git_review('-s')
self._simple_change('a change',
'new change 1 with email but no topic')
self._assert_branch_would_be('master')
finally:
if old_author:
os.environ['GIT_AUTHOR_EMAIL'] = old_author
else:
del os.environ['GIT_AUTHOR_EMAIL']
def test_author_name_topic_bug(self):
old_author = None
if 'GIT_AUTHOR_NAME' in os.environ:
old_author = os.environ['GIT_AUTHOR_NAME']
try:
os.environ['GIT_AUTHOR_NAME'] = 'Bug: #1234'
self._run_git_review('-s')
self._simple_change('a change',
'new change 2 with name but no topic')
self._assert_branch_would_be('master')
finally:
if old_author:
os.environ['GIT_AUTHOR_NAME'] = old_author
else:
del os.environ['GIT_AUTHOR_NAME']
def test_author_email_topic_bug(self):
old_author = None
if 'GIT_AUTHOR_EMAIL' in os.environ:
old_author = os.environ['GIT_AUTHOR_EMAIL']
try:
os.environ['GIT_AUTHOR_EMAIL'] = 'bug5678@example.com'
self._run_git_review('-s')
self._simple_change('a change',
'new change 2 with email but no topic')
self._assert_branch_would_be('master')
finally:
if old_author:
os.environ['GIT_AUTHOR_EMAIL'] = old_author
else:
del os.environ['GIT_AUTHOR_EMAIL']
def test_git_review_T(self):
self._run_git_review('-s')
self._simple_change('test file modified', 'commit message for bug 456')
self._assert_branch_would_be('master/bug/456')
self._assert_branch_would_be('master', extra_args=['-T'])
def test_git_review_T_t(self):
self.assertRaises(Exception, self._run_git_review, '-T', '-t', 'taz')
def test_git_review_l(self):
self._run_git_review('-s')
# Populate "project" repo
self._simple_change('project: test1', 'project: change1, merged')
self._simple_change('project: test2', 'project: change2, open')
self._simple_change('project: test3', 'project: change3, abandoned')
self._run_git_review('-y')
head = self._run_git('rev-parse', 'HEAD')
head_2 = self._run_git('rev-parse', 'HEAD^^')
self._run_gerrit_cli('review', head_2, '--code-review=+2', '--submit')
self._run_gerrit_cli('review', head, '--abandon')
# Populate "project2" repo
self._run_gerrit_cli('create-project', '--empty-commit', '--name',
'test/test_project2')
project2_uri = self.project_uri.replace('test/test_project',
'test/test_project2')
self._run_git('fetch', project2_uri, 'HEAD')
self._run_git('checkout', 'FETCH_HEAD')
self._simple_change('project2: test1', 'project2: change1, open')
self._run_git('push', project2_uri, 'HEAD:refs/for/master')
# Only project1 open changes
result = self._run_git_review('-l')
self.assertNotIn('project: change1, merged', result)
self.assertIn('project: change2, open', result)
self.assertNotIn('project: change3, abandoned', result)
self.assertNotIn('project2:', result)
def _test_git_review_F(self, rebase):
self._run_git_review('-s')
# Populate repo
self._simple_change('create file', 'test commit message')
change1 = self._run_git('rev-parse', 'HEAD')
self._run_git_review()
self._run_gerrit_cli('review', change1, '--code-review=+2', '--submit')
self._run_git('reset', '--hard', 'HEAD^')
# Review with force_rebase
self._run_git('config', 'gitreview.rebase', rebase)
self._simple_change('create file2', 'test commit message 2',
self._dir('test', 'test_file2.txt'))
self._run_git_review('-F')
head_1 = self._run_git('rev-parse', 'HEAD^')
self.assertEqual(change1, head_1)
def test_git_review_F(self):
self._test_git_review_F('1')
def test_git_review_F_norebase(self):
self._test_git_review_F('0')
def test_git_review_F_R(self):
self.assertRaises(Exception, self._run_git_review, '-F', '-R')
def test_config_instead_of_honored(self):
self.set_remote('test_project_url')
self.assertRaises(Exception, self._run_git_review, '-l')
self._run_git('config', '--add', 'url.%s.insteadof' % self.project_uri,
'test_project_url')
self._run_git_review('-l')
def test_config_pushinsteadof_honored(self):
self.set_remote('test_project_url')
self.assertRaises(Exception, self._run_git_review, '-l')
self._run_git('config', '--add',
'url.%s.pushinsteadof' % self.project_uri,
'test_project_url')
self._run_git_review('-l')
class PushUrlTestCase(GitReviewTestCase):
"""Class for the git-review tests using origin push-url."""
_remote = 'origin'
def set_remote(self, uri):
self._run_git('remote', 'set-url', '--push', self._remote, uri)
def reset_remote(self):
self._run_git('config', '--unset', 'remote.%s.pushurl' % self._remote)
def configure_gerrit_remote(self):
self.set_remote(self.project_uri)
self._run_git('config', 'gitreview.usepushurl', '1')
def test_config_pushinsteadof_honored(self):
self.skipTest("pushinsteadof doesn't rewrite pushurls")
class HttpGitReviewTestCase(tests.HttpMixin, GitReviewTestCase):
"""Class for the git-review tests over HTTP(S)."""
pass

View File

@@ -1,348 +0,0 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import functools
import os
import textwrap
import fixtures
import mock
import testtools
from git_review import cmd
from git_review.tests import IsoEnvDir
from git_review.tests import utils
# Use of io.StringIO in python =< 2.7 requires all strings handled to be
# unicode. See if StringIO.StringIO is available first
try:
import StringIO as io
except ImportError:
import io
class ConfigTestCase(testtools.TestCase):
"""Class testing config behavior."""
@mock.patch('git_review.cmd.LOCAL_MODE',
mock.PropertyMock(return_value=True))
@mock.patch('git_review.cmd.git_directories', return_value=['', 'fake'])
@mock.patch('git_review.cmd.run_command_exc')
def test_git_local_mode(self, run_mock, dir_mock):
cmd.git_config_get_value('abc', 'def')
run_mock.assert_called_once_with(
cmd.GitConfigException,
'git', 'config', '-f', 'fake/config', '--get', 'abc.def')
@mock.patch('git_review.cmd.LOCAL_MODE',
mock.PropertyMock(return_value=True))
@mock.patch('os.path.exists', return_value=False)
def test_gitreview_local_mode(self, exists_mock):
cmd.Config()
self.assertFalse(exists_mock.called)
class GitReviewConsole(testtools.TestCase, fixtures.TestWithFixtures):
"""Class for testing the console output of git-review."""
reviews = [
{
'number': '1010101',
'branch': 'master',
'subject': 'A simple short subject',
'topic': 'simple-topic'
}, {
'number': '9877',
'branch': 'stable/codeword',
'subject': 'A longer and slightly more wordy subject'
}, {
'number': '12345',
'branch': 'master',
'subject': 'A ridiculously long subject that can exceed the '
'normal console width, just need to ensure the '
'max width is short enough'
}]
def setUp(self):
# will set up isolated env dir
super(GitReviewConsole, self).setUp()
# ensure all tests get a separate git dir to work in to avoid
# local git config from interfering
iso_env = self.useFixture(IsoEnvDir())
self._run_git = functools.partial(utils.run_git,
chdir=iso_env.work_dir)
self.run_cmd_patcher = mock.patch('git_review.cmd.run_command_status')
run_cmd_partial = functools.partial(
cmd.run_command_status, GIT_WORK_TREE=iso_env.work_dir,
GIT_DIR=os.path.join(iso_env.work_dir, '.git'))
self.run_cmd_mock = self.run_cmd_patcher.start()
self.run_cmd_mock.side_effect = run_cmd_partial
self._run_git('init')
self._run_git('commit', '--allow-empty', '-m "initial commit"')
self._run_git('commit', '--allow-empty', '-m "2nd commit"')
def tearDown(self):
self.run_cmd_patcher.stop()
super(GitReviewConsole, self).tearDown()
@mock.patch('git_review.cmd.query_reviews')
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
@mock.patch('git_review.cmd._has_color', False)
def test_list_reviews_output(self, mock_query):
mock_query.return_value = self.reviews
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
cmd.list_reviews(None)
console_output = output.getvalue().split('\n')
self.assertEqual(
['1010101 master A simple short subject',
' 9877 stable/codeword A longer and slightly more wordy '
'subject'],
console_output[:2])
@mock.patch('git_review.cmd.query_reviews')
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
@mock.patch('git_review.cmd._has_color', False)
def test_list_reviews_output_with_topic(self, mock_query):
mock_query.return_value = self.reviews
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
cmd.list_reviews(None, with_topic=True)
console_output = output.getvalue().split('\n')
self.assertEqual(
['1010101 master simple-topic A simple short subject',
' 9877 stable/codeword - A longer and slightly '
'more wordy subject'],
console_output[:2])
@mock.patch('git_review.cmd.query_reviews')
@mock.patch('git_review.cmd.get_remote_url', mock.MagicMock)
@mock.patch('git_review.cmd._has_color', False)
def test_list_reviews_no_blanks(self, mock_query):
mock_query.return_value = self.reviews
with mock.patch('sys.stdout', new_callable=io.StringIO) as output:
cmd.list_reviews(None)
console_output = output.getvalue().split('\n')
wrapper = textwrap.TextWrapper(replace_whitespace=False,
drop_whitespace=False)
for text in console_output:
for line in wrapper.wrap(text):
self.assertEqual(line.isspace(), False,
"Extra blank lines appearing between reviews"
"in console output")
@mock.patch('git_review.cmd._use_color', None)
def test_color_output_disabled(self):
"""Test disabling of colour output color.ui defaults to enabled
"""
# git versions < 1.8.4 default to 'color.ui' being false
# so must be set to auto to correctly test
self._run_git("config", "color.ui", "auto")
self._run_git("config", "color.review", "never")
self.assertFalse(cmd.check_use_color_output(),
"Failed to detect color output disabled")
@mock.patch('git_review.cmd._use_color', None)
def test_color_output_forced(self):
"""Test force enable of colour output when color.ui
is defaulted to false
"""
self._run_git("config", "color.ui", "never")
self._run_git("config", "color.review", "always")
self.assertTrue(cmd.check_use_color_output(),
"Failed to detect color output forcefully "
"enabled")
@mock.patch('git_review.cmd._use_color', None)
def test_color_output_fallback(self):
"""Test fallback to using color.ui when color.review is not
set
"""
self._run_git("config", "color.ui", "always")
self.assertTrue(cmd.check_use_color_output(),
"Failed to use fallback to color.ui when "
"color.review not present")
class FakeResponse(object):
def __init__(self, code, text=""):
self.status_code = code
self.text = text
class FakeException(Exception):
def __init__(self, code, *args, **kwargs):
super(FakeException, self).__init__(*args, **kwargs)
self.code = code
FAKE_GIT_CREDENTIAL_FILL = """\
protocol=http
host=gerrit.example.com
username=user
password=pass
"""
class ResolveTrackingUnitTest(testtools.TestCase):
"""Class for testing resolve_tracking."""
def setUp(self):
testtools.TestCase.setUp(self)
patcher = mock.patch('git_review.cmd.run_command_exc')
self.addCleanup(patcher.stop)
self.run_command_exc = patcher.start()
def test_track_local_branch(self):
'Test that local tracked branch is not followed.'
self.run_command_exc.side_effect = [
'',
'refs/heads/other/branch',
]
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
(u'remote', u'rbranch'))
def test_track_untracked_branch(self):
'Test that local untracked branch is not followed.'
self.run_command_exc.side_effect = [
'',
'',
]
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
(u'remote', u'rbranch'))
def test_track_remote_branch(self):
'Test that remote tracked branch is followed.'
self.run_command_exc.side_effect = [
'',
'refs/remotes/other/branch',
]
self.assertEqual(cmd.resolve_tracking(u'remote', u'rbranch'),
(u'other', u'branch'))
def test_track_git_error(self):
'Test that local tracked branch is not followed.'
self.run_command_exc.side_effect = [cmd.CommandFailed(1, '', [], {})]
self.assertRaises(cmd.CommandFailed,
cmd.resolve_tracking, u'remote', u'rbranch')
class GitReviewUnitTest(testtools.TestCase):
"""Class for misc unit tests."""
@mock.patch('requests.get', return_value=FakeResponse(404))
def test_run_http_exc_raise_http_error(self, mock_get):
url = 'http://gerrit.example.com'
try:
cmd.run_http_exc(FakeException, url)
self.fails('Exception expected')
except FakeException as err:
self.assertEqual(cmd.http_code_2_return_code(404), err.code)
mock_get.assert_called_once_with(url)
@mock.patch('requests.get', side_effect=Exception())
def test_run_http_exc_raise_unknown_error(self, mock_get):
url = 'http://gerrit.example.com'
try:
cmd.run_http_exc(FakeException, url)
self.fails('Exception expected')
except FakeException as err:
self.assertEqual(255, err.code)
mock_get.assert_called_once_with(url)
@mock.patch('git_review.cmd.run_command_status')
@mock.patch('requests.get', return_value=FakeResponse(200))
def test_run_http_exc_without_auth(self, mock_get, mock_run):
url = 'http://user@gerrit.example.com'
cmd.run_http_exc(FakeException, url)
self.assertFalse(mock_run.called)
mock_get.assert_called_once_with(url)
@mock.patch('git_review.cmd.run_command_status',
return_value=(0, FAKE_GIT_CREDENTIAL_FILL))
@mock.patch('requests.get',
side_effect=[FakeResponse(401), FakeResponse(200)])
def test_run_http_exc_with_auth(self, mock_get, mock_run):
url = 'http://user@gerrit.example.com'
cmd.run_http_exc(FakeException, url)
mock_run.assert_called_once_with('git', 'credential', 'fill',
stdin='url=%s' % url)
calls = [mock.call(url), mock.call(url, auth=('user', 'pass'))]
mock_get.assert_has_calls(calls)
@mock.patch('git_review.cmd.run_command_status',
return_value=(0, FAKE_GIT_CREDENTIAL_FILL))
@mock.patch('requests.get', return_value=FakeResponse(401))
def test_run_http_exc_with_failing_auth(self, mock_get, mock_run):
url = 'http://user@gerrit.example.com'
try:
cmd.run_http_exc(FakeException, url)
self.fails('Exception expected')
except FakeException as err:
self.assertEqual(cmd.http_code_2_return_code(401), err.code)
mock_run.assert_called_once_with('git', 'credential', 'fill',
stdin='url=%s' % url)
calls = [mock.call(url), mock.call(url, auth=('user', 'pass'))]
mock_get.assert_has_calls(calls)
@mock.patch('git_review.cmd.run_command_status',
return_value=(1, ''))
@mock.patch('requests.get', return_value=FakeResponse(401))
def test_run_http_exc_with_failing_git_creds(self, mock_get, mock_run):
url = 'http://user@gerrit.example.com'
try:
cmd.run_http_exc(FakeException, url)
self.fails('Exception expected')
except FakeException as err:
self.assertEqual(cmd.http_code_2_return_code(401), err.code)
mock_run.assert_called_once_with('git', 'credential', 'fill',
stdin='url=%s' % url)
mock_get.assert_called_once_with(url)
@mock.patch('sys.argv', ['argv0', '--track', 'branch'])
@mock.patch('git_review.cmd.check_remote')
@mock.patch('git_review.cmd.resolve_tracking')
def test_command_line_no_track(self, resolve_tracking, check_remote):
check_remote.side_effect = Exception()
self.assertRaises(Exception, cmd._main)
self.assertFalse(resolve_tracking.called)
@mock.patch('sys.argv', ['argv0', '--track'])
@mock.patch('git_review.cmd.check_remote')
@mock.patch('git_review.cmd.resolve_tracking')
def test_track(self, resolve_tracking, check_remote):
check_remote.side_effect = Exception()
self.assertRaises(Exception, cmd._main)
self.assertTrue(resolve_tracking.called)

View File

@@ -1,84 +0,0 @@
# Copyright (c) 2013 Mirantis Inc.
#
# 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 subprocess
import traceback
def run_cmd(*args, **kwargs):
"""Run command and check the return code."""
preexec_fn = None
if 'chdir' in kwargs:
def preexec_fn():
return os.chdir(kwargs['chdir'])
try:
proc = subprocess.Popen(args, stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, env=os.environ,
preexec_fn=preexec_fn)
if 'confirm' in kwargs and kwargs['confirm']:
proc.stdin.write('yes'.encode())
proc.stdin.flush()
out, err = proc.communicate()
out = out.decode('utf-8')
except Exception:
raise Exception(
"Exception while processing the command:\n%s.\n%s" %
(' '.join(args), traceback.format_exc())
)
if proc.returncode != 0:
raise Exception(
"Error occurred while processing the command:\n%s.\n"
"Stdout: %s\nStderr: %s" %
(' '.join(args), out.strip(), err)
)
return out.strip()
def run_git(command, *args, **kwargs):
"""Run git command with the specified args."""
return run_cmd("git", command, *args, **kwargs)
def write_to_file(path, content):
"""Create (if does not exist) and write to the file."""
with open(path, 'wb') as file_:
file_.write(content)
GERRIT_CONF_TMPL = """
[gerrit]
basePath = git
canonicalWebUrl = http://nonexistent/
[database]
type = h2
database = db/ReviewDB
[auth]
type = DEVELOPMENT_BECOME_ANY_ACCOUNT
[sshd]
listenAddress = %s:%s
[httpd]
listenUrl = http://%s:%s/
"""
def get_gerrit_conf(ssh_addr, ssh_port, http_addr, http_port):
return GERRIT_CONF_TMPL % (ssh_addr, ssh_port, http_addr, http_port)

View File

@@ -1 +0,0 @@
requests>=1.1

View File

@@ -1,42 +0,0 @@
[metadata]
name = git-review
summary = Tool to submit code to Gerrit
description-file = README.rst
license = Apache License (2.0)
classifiers =
Programming Language :: Python :: 2
Programming Language :: Python :: 3
Programming Language :: Python
Development Status :: 5 - Production/Stable
Environment :: Console
Environment :: OpenStack
Intended Audience :: Developers
Intended Audience :: Information Technology
License :: OSI Approved :: Apache Software License
Operating System :: OS Independent
keywords = git gerrit review
author = OpenStack
author-email = openstack-infra@lists.openstack.org
home-page = http://docs.openstack.org/infra/git-review/
project-url = http://docs.openstack.org/infra/
[files]
packages =
git_review
[entry_points]
console_scripts =
git-review = git_review.cmd:main
[wheel]
universal = 1
[build_sphinx]
source-dir = doc/source
build-dir = doc/build
all_files = 1
[pbr]
manpages =
git-review.1
warnerrors = True

View File

@@ -1,20 +0,0 @@
#!/usr/bin/env python
# Copyright (c) 2010-2011 OpenStack, LLC.
#
# 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 setuptools
setuptools.setup(setup_requires=['pbr'], pbr=True)

View File

@@ -1,7 +0,0 @@
hacking>=0.10.0,<0.11
mock
fixtures>=0.3.14
testrepository>=0.0.18
testtools>=0.9.34
oslosphinx
sphinx>=1.1.2,!=1.2.0,<1.3

33
tox.ini
View File

@@ -1,33 +0,0 @@
[tox]
envlist = py26,py27,py32,py33,py34,pep8
[testenv]
install_command = pip install -U {opts} {packages}
setenv =
VIRTUAL_ENV={envdir}
commands =
python -m git_review.tests.check_test_id_hashes discover --list
python -m git_review.tests.prepare
python setup.py testr --slowest --testr-args='--concurrency=2 {posargs}'
deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:pep8]
commands = flake8
[testenv:sdist]
commands = python setup.py sdist {posargs}
[testenv:docs]
commands = python setup.py build_sphinx
[testenv:venv]
commands = {posargs}
[flake8]
ignore = E125,H202,H405,H904
show-source = True
exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build