-- cgit v1.2.3-1-g7c22 From 387b74f15929efbdce79a6fbf12448f90bb83766 Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Tue, 11 Sep 2007 05:53:25 +0000 Subject: Import layman. --- .svn.ignore | 2 + AUTHORS | 14 + COPYING | 280 ++++++++++++++ ChangeLog | 310 +++++++++++++++ MANIFEST.in | 4 + bin/layman | 37 ++ doc/.svn.ignore | 3 + doc/Makefile | 41 ++ doc/layman.8.xml | 601 +++++++++++++++++++++++++++++ ebuild/layman-1.0.99.ebuild | 77 ++++ etc/Makefile | 4 + etc/cache-private.xml | 25 ++ etc/cache.xml | 531 +++++++++++++++++++++++++ etc/layman.cfg | 53 +++ layman/.svn.ignore | 1 + layman/__init__.py | 1 + layman/action.py | 420 ++++++++++++++++++++ layman/config.py | 300 ++++++++++++++ layman/db.py | 585 ++++++++++++++++++++++++++++ layman/debug.py | 501 ++++++++++++++++++++++++ layman/overlay.py | 247 ++++++++++++ layman/overlays/.svn.ignore | 1 + layman/overlays/__init__.py | 1 + layman/overlays/bzr.py | 66 ++++ layman/overlays/cvs.py | 73 ++++ layman/overlays/darcs.py | 64 +++ layman/overlays/git.py | 63 +++ layman/overlays/mercurial.py | 64 +++ layman/overlays/overlay.py | 266 +++++++++++++ layman/overlays/rsync.py | 68 ++++ layman/overlays/svn.py | 65 ++++ layman/overlays/tar.py | 189 +++++++++ layman/tests/dtest.py | 90 +++++ layman/tests/testfiles/global-overlays.xml | 30 ++ layman/tests/testfiles/layman-test.tar.bz2 | Bin 0 -> 845 bytes layman/tests/testfiles/make.conf | 345 +++++++++++++++++ layman/utils.py | 208 ++++++++++ layman/version.py | 24 ++ setup.py | 21 + 39 files changed, 5675 insertions(+) create mode 100644 .svn.ignore create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 MANIFEST.in create mode 100755 bin/layman create mode 100644 doc/.svn.ignore create mode 100644 doc/Makefile create mode 100644 doc/layman.8.xml create mode 100644 ebuild/layman-1.0.99.ebuild create mode 100644 etc/Makefile create mode 100644 etc/cache-private.xml create mode 100644 etc/cache.xml create mode 100644 etc/layman.cfg create mode 100644 layman/.svn.ignore create mode 100644 layman/__init__.py create mode 100644 layman/action.py create mode 100644 layman/config.py create mode 100644 layman/db.py create mode 100644 layman/debug.py create mode 100644 layman/overlay.py create mode 100644 layman/overlays/.svn.ignore create mode 100644 layman/overlays/__init__.py create mode 100644 layman/overlays/bzr.py create mode 100644 layman/overlays/cvs.py create mode 100644 layman/overlays/darcs.py create mode 100644 layman/overlays/git.py create mode 100644 layman/overlays/mercurial.py create mode 100644 layman/overlays/overlay.py create mode 100644 layman/overlays/rsync.py create mode 100644 layman/overlays/svn.py create mode 100644 layman/overlays/tar.py create mode 100644 layman/tests/dtest.py create mode 100644 layman/tests/testfiles/global-overlays.xml create mode 100644 layman/tests/testfiles/layman-test.tar.bz2 create mode 100644 layman/tests/testfiles/make.conf create mode 100644 layman/utils.py create mode 100644 layman/version.py create mode 100755 setup.py diff --git a/.svn.ignore b/.svn.ignore new file mode 100644 index 0000000..eaea68a --- /dev/null +++ b/.svn.ignore @@ -0,0 +1,2 @@ +MANIFEST +dist diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..8d72c94 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,14 @@ +AUTHORS +------- + +Written by Gunnar Wrobel (wrobel@gentoo.org) + +Originally created after a discussion with Stuart Herbert (stuart@gentoo.org). + +Contributors: +~~~~~~~~~~~~~ + +Stefan Schweizer (genstef@gentoo.org) - git overlay support +Adrian Perez (moebius@connectical.net) - bzr overlay support +Andres Loeh (kosmikus@gentoo.org) - darcs overlay support +Samuel Tardieu (sam@rfc1149.net) - mercurial overlay support diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..5a965fb --- /dev/null +++ b/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ec01be0 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,310 @@ +2007-01-09 Gunnar Wrobel + + * layman/version.py: + + Update to version 1.0.10 + +2007-01-08 Gunnar Wrobel + + * layman/db.py (DB.add, MakeConf.write): + + Added support for the --priority flag. + + Modified order of entries in PORTDIR_OVERLAY. + + * layman/config.py (Config.__init__): + + Added --priority flag. + + * doc/layman.8.xml: + + Added documentation for --priority switch. + +2007-01-05 Gunnar Wrobel + + * layman/db.py: + + Fixed doc tests. + + * layman/config.py: + + Fixed doc tests. + + * layman/action.py: + + Fixed doc tests. + + * layman/overlays/tar.py: + + Fixed doc tests. + +2006-12-30 Gunnar Wrobel + + * layman/config.py (Config.__init__): + + Fixed quietness option. + + * layman/overlays/tar.py (TarOverlay.__init__): + + Fixed default values. + + * layman/overlays/cvs.py (CvsOverlay.__init__): + + Fixed default values. + + * layman/action.py: + + Add support for --quiet/--quietness + + * layman/overlay.py (Overlays.__init__): + + Add support for --quiet/--quietness + + * layman/overlays/overlay.py (Overlay.__init__, Overlay.cmd): + + Add support for --quiet/--quietness + + * layman/db.py (DB.__init__): + + Add support for --quiet/--quietness + + * layman/version.py: + + This will be version 1.0.9 + + * doc/layman.8.xml: + + Added documentation for --quiet/--quietness + +2006-12-29 Gunnar Wrobel + + * layman/config.py (Config): + + Added quietness and modified OUT.info / OUT.warn calls + accordingly (bug #151965) + + * layman/db.py (DB.sync, DB.add): + + Catching error when syncing fails (bugs #148698 and #159051). + + * ebuild/layman-1.0.8.ebuild: + + Fixed postinstall instructions for layman (bug #149867) + + Added subversion "nowebdav" check by cardoe into repository. + +2006-09-23 + + * layman/version.py: Update to 1.0.8 + +2006-09-22 + + * layman/overlays/cvs.py (CvsOverlay.sync): CVS support + +2006-09-05 + + * layman/*: Generic pylint and cleanup fixes + + * layman/config.py (Config.__init__): Modified to allow switching + strict checking off. + +2006-08-08 + + * layman/overlays/tar.py (TarOverlay.add): Support for explicit + file format of a tar package. + +2006-08-01 + + * layman/action.py (Sync.run): Print successes and warning at the + end of the run. + (List.run): List only official overlays when not running verbose. + + * layman/db.py (MakeConf.write.prio_sort): Sort overlays by + priority when writing make.conf + + * layman/overlay.py (Overlays.list): Support for marking overlays + as official. + (Overlays.read): Only warn on invalid overlay definitions and do + not add them. + + * layman/overlays/bzr.py: Allow whitespace. + + * layman/overlays/darcs.py: Allow whitespace. + + * layman/overlays/git.py: Allow whitespace. + + * layman/overlays/mercurial.py: Allow whitespace. + + * layman/overlays/rsync.py: Allow whitespace. + + * layman/overlays/svn.py: Allow whitespace. + + * layman/overlays/tar.py: Allow whitespace. + + * layman/overlays/overlay.py (Overlay.__init__): Required contact, + description. Added support for status and priority. + (Overlay.__str__): Modified display according to changes given + above. + (Overlay.is_official): Added new function for official overlays. + + * layman/overlay.py: Added mercurial support thanks to + Andres Loeh + + * layman/overlays/mercurial.py: Added mercurial support thanks to + Andres Loeh + +2006-07-25 + + * layman/config.py (Config.__init__): Fixed error introduced in + changeset 204. Config file gets read again. + +2006-07-18 + + * layman/version.py: version bump to 1.0.5 + + * layman/config.py (Config.__getitem__): Fixed handling of overlays + specified on the command line. + + * layman/db.py (RemoteDB.cache): Fixed handling of downloaded + overlay list. + + * layman/overlay.py: Added darcs module. + +2006-07-17 + + * layman/config.py (Config.__init__): Fixed some config defaults. + + * layman/action.py (Actions.__init__): Reduced actions for which + the remote lists will be implicitely fetched. + + * doc/layman.8.xml: Updated documentation for implicit fetch. + + * layman/action.py (Actions.__init__): Run fetch implicitely + only on specific actions. + + * layman/db.py (RemoteDB.cache): Modified download code for + improved handling of fetch problems. + + * layman/action.py (Sync.__init__): Added --sync-all support. + + * doc/layman.8.xml: Added --sync-all documentation. + + * layman/db.py (RemoteDB.__init__): Added proxy support. + + * etc/layman.cfg (make_conf): Added local file syntax example. + (overlays): Added proxy variable. + + * doc/layman.8.xml: Added documentation about local overlay + definitions. + Added proxy documentation. + +2006-06-28 + + * layman/version.py: Version bump to 1.0.4 + + * layman/action.py (Sync.run): Fixed src check. + +2006-06-27 + + * layman/action.py (ListLocal.run): Fixed code for listing. + + * doc/layman.8.xml: Fixed documentation for new nofetch option. + + * layman/action.py (Actions.__init__): Made fetching the default + if nofetch is NOT set. + (Sync.run): Added source path comparison for overlay location changes. + (List.run): Modified output to differentiate between supported and + unsupported overlays. + + * layman/config.py (Config.__init__): Added nofetch option. + + * layman/overlay.py: Added bzr support. Thanks to Adrian Perez. + + * layman/overlays/bzr.py: Added bzr support. Thanks to Adrian Perez. + + * layman/overlays/overlay.py (Overlay.short_list): Fixed length + of short listing. + (Overlay.supported): Fixed check for supported overlay types. + +2006-06-05 + + * layman/overlays/overlay.py (Overlay): Forgot to add a check + if the overlay type is actually supported. -> 1.0.2 + + * layman/overlay.py: genstef@gentoo.org provided support for + git based overlays. -> released 1.0.1 + +2006-05-27 + + * doc/layman.8.xml: Updated docs to multi list support + + * layman/version.py: Version bump to 1.0.0 + + * etc/layman.cfg (overlays): Adapted configuration for change + in global list url and multiple list support. + + * layman/config.py (Config.__init__): Fixed link for global + overlay list. + + * layman/db.py: Added support for multiple overlay lists. + + * layman/overlay.py: Added support for mutliple overlay lists. + +2006-05-01 + + * layman/version.py: Version bump to 0.9.2 + + * layman/tests/dtest.py (test_suite): Added utils.py test + + * layman/overlays/tar.py (TarOverlay): Fixed testing results. + +2006-04-14 + + * layman/version.py: Version 0.9.1 + + * layman/overlays/tar.py (TarOverlay.__init__): Added + category support to packaged overlays. Could/Should + also allow this for other overlay types. + +2006-03-12 + + * layman/db.py (RemoteDB.cache): Create storage + dir if it is missing. + (MakeConf.write): Add $PORTDIR_OVERLAY to include + manual PORTDIR_OVERLAY variable. + (MakeConf.read): Disregard PORTDIR_OVERLAY variable + as overlay name. + + * layman/action.py (Fetch.run): Added error handling + for fetch problems. + + * doc/layman.8.xml: Fixes to the man page. + + * layman/version.py: Version bump to 0.9 + + * layman/action.py (Add.run): Added warning if + overlay is unkown. + + * etc/cache.xml: Added kolab2 and xgloverlays. + + * layman/overlays/overlay.py (Overlay.short_list): Added + shortened overlay listing + + * layman/overlay.py: Added tar support. + + * layman/overlays/tar.py: Added tar support. + +2006-02-04 + + * etc/cache-private.xml: Added private overlay locations. + + * layman/db.py (MakeConf.read): Allowed for a missing + make.conf. This now allows to source an external file + into the original make.conf + + * etc/layman.cfg (cache): Fixed location of cache and + overlay list + + * layman/config.py (Config.__init__): Fixed description of + layman usage. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..5829965 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,4 @@ +include etc/layman.cfg +include doc/layman.8 +include doc/layman.8.html +recursive-include layman/tests * diff --git a/bin/layman b/bin/layman new file mode 100755 index 0000000..3080c05 --- /dev/null +++ b/bin/layman @@ -0,0 +1,37 @@ +#!python +################################################################################ +# LAYMAN - A UTILITY TO SELECT AND UPDATE GENTOO OVERLAYS +################################################################################ +# Distributed under the terms of the GNU General Public License v2 +# +# Copyright: +# (c) 2005 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# + +__version__ = "$Id$" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.config import Config +from layman.action import Actions + +#=============================================================================== +# +# MAIN +# +#------------------------------------------------------------------------------- + +def main(): + + Actions(Config()) + +if __name__ == "__main__": + main() diff --git a/doc/.svn.ignore b/doc/.svn.ignore new file mode 100644 index 0000000..ff0e9f9 --- /dev/null +++ b/doc/.svn.ignore @@ -0,0 +1,3 @@ +*.html +*.8 +semantic.cache diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..64b28af --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,41 @@ +# +# layman/doc/Makefile +# Simple Makefile to rebuild the documentation from the +# docbook XML sources +# +# Copyright (c) 1999-2005 Gentoo Foundation +# Released under v2 of the GNU GPL +# +# Author(s) Stuart Herbert +# Renat Lumpau +# Gunnar Wrobel +# +# ======================================================================== + +MAN_PAGES = layman.8 +HTML_PAGES = layman.8.html + +TMPFILE=./layman.man + +all: man html + +html: $(HTML_PAGES) + +man: $(MAN_PAGES) + +clean: + rm -f $(MAN_PAGES) + rm -f $(HTML_PAGES) + +%.html: %.xml + @echo HTML $@ + @xmlto html-nochunks $< + +%: %.xml + @echo MAN $@ + @xmlto man $< +# +# fix up the blank lines that docbook leaves behind +# + @cat $@ | sed -e 's/$$/.fred/g;' | tr -d '\n' | sed -e 's/.fred.fred\./.fred./g;' | sed -e 's/.fred/\n/g;' > $(TMPFILE) + @mv $(TMPFILE) $@ diff --git a/doc/layman.8.xml b/doc/layman.8.xml new file mode 100644 index 0000000..c966786 --- /dev/null +++ b/doc/layman.8.xml @@ -0,0 +1,601 @@ + + +
+ + + layman + + + + Gunnar + Wrobel + +
+ wrobel@gentoo.org + + + +
+
+
+
+ + + 2006 + Gunnar Wrobel + +
+ +
+ Reference + + + + layman + February 2006 + Gentoo Linux + + + layman + 8 + + + layman + + manage your local repository of gentoo overlays + + + + + + layman + + -a + --add + + + ALL + overlay + + + + + layman + + -d + --delete + + + ALL + overlay + + + + + layman + + -s + --sync + + + ALL + overlay + + + + + layman + + -S + --sync-all + + + + + layman + + -L + --list + + + + + layman + + -l + --list-local + + + + + layman + + -f + --fetch + + + + + layman + + -n + --nofetch + + + + + layman + + -k + --nocheck + + + + + layman + + -q + --quiet + + + + + layman + + -Q + --quietness + + 0-4 + + + + + + + + Description + + layman is a script that allows you to + add, remove and update gentoo overlays from a variety of + sources. + + + + + WARNING + + layman makes it easy to retrieve and + update overlays for gentoo. In addition it makes it TRIVIAL + to break your system. + + + The main portage tree provides you with high quality ebuilds + that are all maintained by gentoo developers. This will not + be the case for most of the overlays you can get by using + layman. Thus you are removing the + security shield that the standard tree provides for + you. You should keep that in mind when installing ebuilds + from an overlay. + + + To ensure the security of your system you MUST read the + source of the ebuild you are about to install. + + + + + + + Handling overlays + + layman intends to provide easy + maintenance of gentoo overlays while not requiring any + configuration. + + + + + Remote overlay lists + + layman allows you to fetch an overlay + without the need to modify any configuration files. In + order for this to be possible the script needs an external + list of possible overlay sources. There will be a + centralized list available here, + but nothing will prevent you from using or publishing your + own list of overlays. The location of the remote lists can + also be modified using the option + when running layman. + + + To get a new overlay added to the central list provided + for layman, send a mail to + overlays@gentoo.org. Gentoo developers may + add their overlay entries directly into the list which can + be accessed over the CVS repository for the Gentoo + website. + + + You can also use several lists at the same time. Just + add one url per line to the overlays variable in your + configuration file. layman will merge the + contents of all lists. + + + layman also allows you to define + local files in this list. Just make sure you prepend these + pathnames in standard URL notation + with file://. + + + If you need to use a proxy for access to the internet, + you can use the corresponding variable in + the layman configuration file. Layman + will also respect the http_proxy + environment variable in case you set it. + + + + + + + Local cache + + layman stores a local copy of the + fetched remote list. It will be stored in + /usr/portage/local/layman/cache.xml + by default. There exists only one such cache file and it + will be overwritten every time you + run layman. + + + + + + + Handling <filename>/etc/make.conf</filename> + + Since layman is designed to + automatically handle the inclusion of overlays into your + system it needs to be able to modify + the PORTDIR_OVERLAY variable in your + /etc/make.conf file. But + /etc/make.conf is a very central and + essential configuration file for a gentoo + system. Automatically modifying this file would be + somewhat dangerous. You can + allow layman to do this by setting + the make_conf variable in the + configuration file to /etc/make.conf. + + + A much safer and in fact recommended solution to the + problem is to let layman handle an + external file that only contains + the PORTDIR_OVERLAY variable and is + sourced within the + standard /etc/make.conf file. Just add the following line to the end of your + /etc/make.conf file: + + + source /usr/portage/local/layman/make.conf + + /usr/portage/local/layman/make.conf + is the default provided in the layman + configuration. Change this filename in case you decide to + store it somewhere else. + + + The file does not necessarily need to exist at the + beginning. If it is missing, layman will create it for you. + + + There is also no need to remove the + original PORTDIR_OVERLAY variable from + the make.conf file. Layman will simply add new overlays to + this variable and all your old entries will remain in there. + + + + + + + Adding, removing and updating overlays + + Once a remote list of overlays has been fetched, + layman allows to add overlays from the + remote list to your system. The script will try to fetch + the overlay. If this is successful the overlay information + will be copied from the cache to the list of locally + installed overlays. In addition + layman will modify the + PORTDIR_OVERLAY variable to include the + new overlay path. + + + Removing the overlay with layman will + delete the overlay without leaving any traces behind. + + + In order to update all overlays managed by + layman you can run the script with the + option or + the flag. + + + + + + + List overlays + + layman provides the + and + options to print a list of available respectively + installed overlays. + + + Listing will prepend all fully supported overlays + with a green asterisk, all non-official overlays with a + yellow asterisk and all overlays that you will not be able + to use since you do not have the necessary tools installed + with a red asterisk. + + + In the default mode layman will be strict about + listing overlays and only present you with overlays that + are fully supported. In addition it will complain about + overlays that are missing a description field or a contact + attribute. This type of behaviour has been added with + layman-1.0.7 and if you'd like to return to the old + behaviour you may use the k option flag or set the nocheck + option in the configuration file. + + + + + + + Overlay types + + Currently layman supports overlays that + are exported via rsync, + subversion, bzr, + darcs, git, + mercurial or provided + as tar packages. + + + + + + + + + Actions + + List of possible layman actions. + + + + + + + Fetches the remote list of overlays. You will + usually NOT need to explicitely specify this option. The + fetch operation will be performed automatically once you + run the sync, sync-all, or list action. You can prevent + this automatic fetching using the --nofetch option. + + + + + overlay + overlay + + Add the given overlay from the cached remote list to + your locally installed overlays. Specify "ALL" to add + all overlays from the remote list. + + + + + overlay + overlay + + Remove the given overlay from your locally installed + overlays. Specify "ALL" to remove all overlays + + + + + overlay + overlay + + Update the specified overlay. Use "ALL" as + parameter to synchronize all overlays + + + + + + + + Update all overlays. Shortcut for -s ALL. + + + + + + + + List the contents of the remote list. + + + + + + + + List the locally installed overlays. + + + + + + + + + Options + + List of available layman options. + + + + + path + path + + Path to an alternative configuration file. + + + + + url + url + + Specifies the location of additional overlay + lists. You can use this flag several times and the + specified urls will get appended to the list of urls you + specified in your config file. You may also specify + local file urls by prepending the path with + file:// + + + + + + + + Prevents layman from + automatically fetching the remote lists of overlays. The + default behaviour for layman is to + update all remote lists if you run the sync, list or + fetch operation. + + + + + + + + Prevents layman from checking + the remote lists of overlays for complete overlay + definitions. The default behaviour for layman is to + reject overlays that do not provide a description or a + contact attribute. + + + + + + + + Makes layman completely quiet. + This option is dangerous: If the processes spawned by + layman when adding or synchronizing overlays require + any input layman will hang without telling you + why. This might happen for example if your overlay + resides in subversion and the SSL certificate of + the server needs acceptance. + + + + + LEVEL + LEVEL + + Makes layman less verbose. + Choose a value between 0 and 4 with 0 being completely + quiet. Once you set this below 3, the same warning as + given for --quiet applies. + + + + + LEVEL + LEVEL + + Use this option in combination with + the --add. It will modify the + priority of the added overlay and thus influence the + order of entries in the make.conf file. The lower the + priority, the earlier in the list the entry will be + mentioned. Use a value between 0 and 100. The default + value is 50. + + + + + + + + + + Examples + + + + Installing an overlay + + layman -f -a wrobel + This would add the overlay with the id + wrobel to your list of installed + overlays. + + + + + + Syncing your overlays + + layman -s ALL + This updates all overlays + + + + + + Performing several actions at the same time + + layman -f -a wrobel -a webapps-experimental + This fetches the remote list and immediately adds two + overlays + + + + + + + + Files + + + + + /etc/layman/layman.cfg + + Configuration file, holding the defaults for + layman + + + + + + + + +
+
diff --git a/ebuild/layman-1.0.99.ebuild b/ebuild/layman-1.0.99.ebuild new file mode 100644 index 0000000..f246993 --- /dev/null +++ b/ebuild/layman-1.0.99.ebuild @@ -0,0 +1,77 @@ +# Copyright 1999-2007 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/app-portage/layman/layman-1.0.10.ebuild,v 1.1 2007/01/09 18:01:15 wrobel Exp $ + +inherit eutils distutils + +DESCRIPTION="A python script for retrieving gentoo overlays." +HOMEPAGE="http://projects.gunnarwrobel.de/scripts" +SRC_URI="http://build.pardus.de/downloads/${PF}.tar.gz" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="~alpha ~amd64 ~ppc ~ppc64 ~sparc ~x86 ~x86-fbsd" +IUSE="" +S=${WORKDIR}/${PF} + +DEPEND="" +RDEPEND="" + +pkg_setup() { + if has_version dev-util/subversion && built_with_use dev-util/subversion nowebdav; then + eerror "You must rebuild your subversion without the nowebdav USE flag" + die "You must rebuild your subversion without the nowebdav USE flag" + fi +} + +src_install() { + + distutils_src_install + + dodir /etc/layman + cp etc/* ${D}/etc/layman/ + + doman doc/layman.8 + dohtml doc/layman.8.html + +} + +src_test() { + cd ${S} + einfo "Running layman doctests..." + echo + if ! PYTHONPATH="." ${python} layman/tests/dtest.py; then + eerror "DocTests failed - please submit a bug report" + die "DocTesting failed!" + fi +} + +pkg_postinst() { + einfo "You are now ready to add overlays into your system." + einfo + einfo "layman -L" + einfo + einfo "will display a list of available overlays." + einfo + ewarn "Use the -k switch to also list overlays that are" + ewarn "considered less secure." + einfo + elog "Select an overlay and add it using" + einfo + elog "layman -a overlay-name" + einfo + elog "If this is the very first overlay you add with layman," + elog "you need to append the following statement to your" + elog "/etc/make.conf file:" + elog + elog "source /usr/portage/local/layman/make.conf" + elog + elog "If you modify the 'storage' parameter in the layman" + elog "configuration file (/etc/layman/layman.cfg) you will" + elog "need to adapt the path given above to the new storage" + elog "directory." + einfo + ewarn "Please add the 'source' statement to make.conf only AFTER " + ewarn "you added your first overlay. Otherwise portage will fail." + epause 5 +} diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 0000000..4a8c01a --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,4 @@ +list: + scp cache-private.xml root@build.pardus.de:/var/www/localhost/htdocs/downloads/private-overlays.xml + +.PHONY: list diff --git a/etc/cache-private.xml b/etc/cache-private.xml new file mode 100644 index 0000000..d1bf56a --- /dev/null +++ b/etc/cache-private.xml @@ -0,0 +1,25 @@ + + + + + + + + + + diff --git a/etc/cache.xml b/etc/cache.xml new file mode 100644 index 0000000..2f4759d --- /dev/null +++ b/etc/cache.xml @@ -0,0 +1,531 @@ + + + + + + + http://gentopia.gentooexperimental.org/ + + + + Purpose + + To provide a smooth, seamlessly functional and enjoyable Gentoo + Desktop experience by following the mantra of "Just Works". + + + + + + + + http://svn.gnqs.org/projects/gentoo-php-overlay/ + + + + The Gentoo PHP Overlay is our testbed for new and improved PHP + packages for Gentoo21. It's a place where Gentoo developers and + users alike can work together on providing Gentoo Linux with the + very best PHP support that there is, for any platform. + + + + + + + + https://gentooscience.org/ + + + + The Gentoo Science Overlay is intended as a place to work + outside of the main portage tree on experimental ebuilds. Our + aim is to increase the rate of development of scientific + packages for Gentoo, but ebuilds in this repository are by their + very nature more experimental. If you wish to use them you + should be willing to help test and report bugs. + + + + + + + + + + + http://www.breakmygentoo.net + + + + Nevertheless the name seems to be standing for something other, + "Break My Gentoo" is not just a project to help you destroying your + fine Gentoo-installation. It could, but this is not the main purpose. + We want to provide unstable ebuilds, which have no chance to get into + portage. We want to give technology previews especially focused on + GNOME. + + + + + + + + + + + Contains A LOT of *-cvs packages for many things, read the + readme file for more information. + + + + + + + + http://catmur.co.uk/gentoo/ + + + + ecatmurs overlay contains a variety of ebuilds: cool applications, + various fixes, version bumps, bleeding-edge code grabs. + + + + + + + + http://genkdesvn.berlios.de/wiki/index.php/Main_Page + + + + This wiki has been provided as a central place for information + regarding the kde-svn ebuilds. + + + + + + + + http://genkdesvn.berlios.de/wiki/index.php/Main_Page + + + + This wiki has been provided as a central place for information + regarding the kde-svn ebuilds. + + + + + + + + http://gentoo.zugaina.org/ + + + + + + + + + + + http://www.ebuildexchange.org/ + + + + This site tries to provide a new medium of distributing Ebuilds + for the Gentoo Linux Distribution. + + + + + + + + http://wiki.erazor-zone.de/wiki:projects:linux:gentoo + + + + erazor provides some ebuilds here. At this point, most of them + are newer that the ones in portage, but be warned, they have not + been tested much and there is no warranty at all. + + + + + + + + http://fantoo.org/ + + + + Portage Overlay tree of new gentoo based distibution called + Fantoo Linux. Fantoo Linux aims to be bleeding edge and bugfixes + in gentoo upstream. + + + + + + + + http://svn.gnqs.org/projects/gentoo-webapps-overlay/ + + + + This is the home of Gentoo's wider collection of ebuilds for + web-based applications. This is where we collect all the ebuilds + submitted to Bugzilla by our users, and make them available in + an easy-to-use overlay for wider testing. + + + + + + + + http://projects.gunnarwrobel.de/ebuilds/wiki/WikiStart + + + + A collection of stable ebuilds from Gunnar Wrobel [wrobel@gentoo.org]. + + + + + + + + http://projects.gunnarwrobel.de/ebuilds/wiki/WikiStart + + + + A collection of experimental ebuilds from Gunnar Wrobel [wrobel@gentoo.org]. + + + + + + + + http://projects.gunnarwrobel.de/kolab/wiki + + + + Project to allow running Kolab2 on Gentoo. + + + + + + + + http://gentoo-wiki.com/XGL#Checkout_An_Overlay + + + + Xgl is an Xserver that uses OpenGL for its drawing + operations. Together with compiz, an opengl compositing and + window manager, it allows for some cool desktop effects. + + This is the XGL overlay from Hanno Boeck (http://www.hboeck.de) + + + + + + + + http://svn.netdomination.org/gentoo-voip + + + + Voice over IP related ebuilds. + + + + + + + + https://kpex.no-ip.org/kpex-media.html + + + + A series of unsupported multimedia ebuilds for Gentoo + portage that are either updated versions, live CVS/SVN + packages, or have updated features not included in the + official portage tree. + + + + + + + + http://www.initng.org + + + + Initng is a full replacement of the old and in many ways + deprecated sysvinit tool. It is designed with speed in mind + because it does as much as possible asynchronously. In other + words: It will boot your unix-system much faster, and give you + more control and statistics over your system. + + + + + + + + http://forums.gentoo.org/viewtopic-t-435659-start-0-postdays-0-postorder-asc-highlight-nxsty+glibc.html + + + + This overlay provides an enhanced glibc 2.4 ebuild. + + + + + + + + http://forums.gentoo.org/viewtopic-t-435659-start-0-postdays-0-postorder-asc-highlight-nxsty+glibc.html + + + + This overlay provides a supplemental binutils ebuild to the nxsty-glibc overlay. + + + + + + + + http://svn.gnqs.org/projects/gentoo-nx-overlay/timeline + + + + Overlay for the NX/FreeNX packages for Gentoo. + + + + + + + + http://forums.gentoo.org/viewtopic-t-427211.html + + + + Evermind's overlay with ebuilds for a lot of pro-audio production + software. + + + + + + + + http://gentoo-wiki.com/HOWTO_Catalyst_Framework + + + + This project tries to provide ebuilds for the great Catalyst MVC + Framework and many of the related Perl modules, plugins, etc. + + + + + + + + http://bugs.gentoo.org/show_bug.cgi?id=122500 + + + + Testing ground for vmware-server and vmware-server-console + ebuilds until they can eventually be committed into portage. + + + + + + + + https://projects.gentooexperimental.org/expj/wiki + + + + The current focus of this overlay is on migration to a new build + system which will ultimately allow us to unmask Java 1.5, and be + able to support Java 1.6 when it is released. + + + + + + + + https://projects.gentooexperimental.org/expj/wiki + + + + This is the experimental Portage tree for the Gentoo Java + Team. Half-baked ebuilds in-progress will reside it here during + development time, before they are included into the + + + + + + + + http://juffo.org/proj/musicbrainz-overlay/README.html + + + + This is the Gentoo Portage overlay for the most recent + MusicBrainz packages. + + + + + + + + http://liferea.sourceforge.net/ + + + + Updated ebuilds for newer versions of Liferea + + + + + + + + http://developer.berlios.de/projects/yao/ + + + + Yet Another Overlay aims to provide ebuilds for light + applications to allow Gentoo users to maintain a light desktop + without being restricted to the small amount of apps which don't + depend on GNOME or KDE libraries. + + + + + diff --git a/etc/layman.cfg b/etc/layman.cfg new file mode 100644 index 0000000..51f2e70 --- /dev/null +++ b/etc/layman.cfg @@ -0,0 +1,53 @@ +[MAIN] + +#----------------------------------------------------------- +# Path to the config directory + +config_dir: /etc/layman + +#----------------------------------------------------------- +# Defines the directory where overlays should be installed + +storage : /usr/portage/local/layman + +#----------------------------------------------------------- +# Remote overlay lists will be stored here +# layman will append _md5(url).xml to each filename + +cache : %(storage)s/cache + +#----------------------------------------------------------- +# The list of locally installed overlays + +local_list: %(storage)s/overlays.xml + +#----------------------------------------------------------- +# Path to the make.conf file that should be modified by +# layman + +make_conf : %(storage)s/make.conf + +#----------------------------------------------------------- +# URLs of the remote lists of overlays (one per line) or +# local overlay definitions +# +#overlays : http://www.gentoo.org/proj/en/overlays/layman-global.txt +# http://dev.gentoo.org/~wrobel/layman/global-overlays.xml +# http://mydomain.org/my-layman-list.xml +# file:///usr/portage/local/layman/my-list.xml + +overlays : http://www.gentoo.org/proj/en/overlays/layman-global.txt + +#----------------------------------------------------------- +# Proxy support +# +#proxy : http://[user:pass@]www.my-proxy.org:3128 + +#----------------------------------------------------------- +# Strict checking of overlay definitions +# +# Set either to "yes" or "no". If "no" layman will issue +# warnings if an overlay definition is missing either +# description or contact information. +# +nocheck : no diff --git a/layman/.svn.ignore b/layman/.svn.ignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/layman/.svn.ignore @@ -0,0 +1 @@ +*.pyc diff --git a/layman/__init__.py b/layman/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/layman/__init__.py @@ -0,0 +1 @@ +# diff --git a/layman/action.py b/layman/action.py new file mode 100644 index 0000000..b2c67ce --- /dev/null +++ b/layman/action.py @@ -0,0 +1,420 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN ACTIONS +################################################################################# +# File: action.py +# +# Handles layman actions. +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Provides the different actions that can be performed by layman.''' + +__version__ = "$Id: action.py 312 2007-04-09 19:45:49Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import sys + +from layman.db import DB, RemoteDB + +from layman.debug import OUT + +#=============================================================================== +# +# Class Fetch +# +#------------------------------------------------------------------------------- + +class Fetch: + ''' Fetches the overlay listing. + + >>> import os + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> cache = os.tmpnam() + >>> config = {'overlays' : + ... 'file://' + here + '/tests/testfiles/global-overlays.xml', + ... 'cache' : cache, + ... 'nocheck' : True, + ... 'proxy' : None, + ... 'quietness':3} + >>> a = Fetch(config) + >>> a.run() + 0 + >>> b = open(a.db.path(config['overlays'])) + >>> b.readlines()[24] + ' A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].\\n' + + >>> b.close() + >>> os.unlink(a.db.path(config['overlays'])) + + >>> a.db.overlays.keys() + [u'wrobel', u'wrobel-stable'] + ''' + + def __init__(self, config): + self.db = RemoteDB(config) + + def run(self): + '''Fetch the overlay listing.''' + try: + self.db.cache() + except Exception, error: + OUT.die('Failed to fetch overlay list!\nError was: ' + + str(error)) + + return 0 + +#=============================================================================== +# +# Class Sync +# +#------------------------------------------------------------------------------- + +class Sync: + ''' Syncs the selected overlays.''' + + def __init__(self, config): + + self.db = DB(config) + + self.rdb = RemoteDB(config) + + self.selection = config['sync'] + + if config['sync_all'] or 'ALL' in self.selection: + self.selection = self.db.overlays.keys() + + def run(self): + '''Synchronize the overlays.''' + + OUT.debug('Updating selected overlays', 6) + + warnings = [] + success = [] + for i in self.selection: + ordb = self.rdb.select(i) + odb = self.db.select(i) + if ordb and odb and ordb.src != odb.src: + warnings.append( + 'The source of the overlay "' + i + '" seems to have c' + 'hanged. You currently sync from "' + odb.src + '" whi' + 'le the global layman list reports "' + ordb.src + '" ' + 'as correct location. Please consider removing and rea' + 'dding the overlay!') + + try: + self.db.sync(i) + success.append('Successfully synchronized overlay "' + i + '".') + except Exception, error: + warnings.append( + 'Failed to sync overlay "' + i + '".\nError was: ' + + str(error)) + + if success: + OUT.info('\nSuccess:\n------\n', 3) + for i in success: + OUT.info(i, 3) + + if warnings: + OUT.warn('\nErrors:\n------\n', 2) + for i in warnings: + OUT.warn(i + '\n', 2) + return 1 + + return 0 + +#=============================================================================== +# +# Class Add +# +#------------------------------------------------------------------------------- + +class Add: + ''' Adds the selected overlays.''' + + def __init__(self, config): + + self.config = config + + self.db = DB(config) + + self.rdb = RemoteDB(config) + + self.selection = config['add'] + + if 'ALL' in self.selection: + self.selection = self.rdb.overlays.keys() + + def run(self): + '''Add the overlay.''' + + OUT.debug('Adding selected overlays', 6) + + result = 0 + + for i in self.selection: + overlay = self.rdb.select(i) + + OUT.debug('Selected overlay', 7) + + if overlay: + try: + self.db.add(overlay) + OUT.info('Successfully added overlay "' + i + '".', 2) + except Exception, error: + OUT.warn('Failed to add overlay "' + i + '".\nError was: ' + + str(error), 2) + result = 1 + else: + OUT.warn('Overlay "' + i + '" does not exist!', 2) + result = 1 + + return result + +#=============================================================================== +# +# Class Delete +# +#------------------------------------------------------------------------------- + +class Delete: + ''' Deletes the selected overlays.''' + + def __init__(self, config): + + self.db = DB(config) + + self.selection = config['delete'] + + if 'ALL' in self.selection: + self.selection = self.db.overlays.keys() + + def run(self): + '''Delete the overlay.''' + + OUT.debug('Deleting selected overlays', 6) + + result = 0 + + for i in self.selection: + overlay = self.db.select(i) + + OUT.debug('Selected overlay', 7) + + if overlay: + try: + self.db.delete(overlay) + OUT.info('Successfully deleted overlay "' + i + '".', 2) + except Exception, error: + OUT.warn('Failed to delete overlay "' + i + '".\nError was: ' + + str(error), 2) + result = 1 + else: + OUT.warn('Overlay "' + i + '" does not exist!', 2) + result = 1 + + return result + +#=============================================================================== +# +# Class List +# +#------------------------------------------------------------------------------- + +class List: + ''' Lists the available overlays. + + >>> import os + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> cache = os.tmpnam() + >>> config = {'overlays' : + ... 'file://' + here + '/tests/testfiles/global-overlays.xml', + ... 'cache' : cache, + ... 'proxy' : None, + ... 'nocheck' : False, + ... 'verbose': False, + ... 'quietness':3} + >>> a = List(config) + >>> a.rdb.cache() + >>> OUT.color_off() + >>> a.run() + * wrobel [Subversion] (source: https://overlays.gentoo.or...) + 0 + >>> a.config['verbose'] = True + >>> a.run() + * wrobel + * ~~~~~~ + * Source : https://overlays.gentoo.org/svn/dev/wrobel + * Contact : nobody@gentoo.org + * Type : Subversion; Priority: 10 + * + * Description: + * Test + * + * *** This is no official gentoo overlay *** + * + * wrobel-stable + * ~~~~~~~~~~~~~ + * Source : rsync://gunnarwrobel.de/wrobel-stable + * Contact : nobody@gentoo.org + * Type : Rsync; Priority: 50 + * + * Description: + * A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org]. + * + 0 + ''' + + def __init__(self, config): + + OUT.debug('Creating RemoteDB handler', 6) + + self.rdb = RemoteDB(config) + self.config = config + + def run(self): + ''' List the available overlays.''' + + for i in self.rdb.list(self.config['verbose']): + # Is the overlay supported? + if i[1]: + # Is this an official overlay? + if i[2]: + OUT.info(i[0], 1) + # Unofficial overlays will only be listed if we are not + # checking or listing verbose + elif self.config['nocheck'] or self.config['verbose']: + # Give a reason why this is marked yellow if it is a verbose + # listing + if self.config['verbose']: + OUT.warn('*** This is no official gentoo overlay ***\n', 1) + OUT.warn(i[0], 1) + # Unsupported overlays will only be listed if we are not checking + # or listing verbose + elif self.config['nocheck'] or self.config['verbose']: + # Give a reason why this is marked red if it is a verbose + # listing + if self.config['verbose']: + OUT.error('*** You are lacking the necessary tools to insta' + 'll this overlay ***\n') + OUT.error(i[0]) + + return 0 + +#=============================================================================== +# +# Class ListLocal +# +#------------------------------------------------------------------------------- + +class ListLocal: + ''' Lists the local overlays.''' + + def __init__(self, config): + self.db = DB(config) + self.config = config + + def run(self): + '''List the overlays.''' + + for i in self.db.list(self.config['verbose']): + + OUT.debug('Printing local overlay.', 8) + + # Is the overlay supported? + if i[1]: + # Is this an official overlay? + if i[2]: + OUT.info(i[0], 1) + # Unofficial overlays will only be listed if we are not + # checking or listing verbose + else: + # Give a reason why this is marked yellow if it is a verbose + # listing + if self.config['verbose']: + OUT.warn('*** This is no official gentoo overlay ***\n', 1) + OUT.warn(i[0], 1) + # Unsupported overlays will only be listed if we are not checking + # or listing verbose + else: + # Give a reason why this is marked red if it is a verbose + # listing + if self.config['verbose']: + OUT.error('*** You are lacking the necessary tools to insta' + 'll this overlay ***\n') + OUT.error(i[0]) + + return 0 + +#=============================================================================== +# +# Class Actions +# +#------------------------------------------------------------------------------- + +class Actions: + '''Dispatches to the actions the user selected. ''' + + # Given in order of precedence + actions = [('fetch', Fetch), + ('add', Add), + ('sync', Sync), + ('sync_all', Sync), + ('delete', Delete), + ('list', List), + ('list_local', ListLocal),] + + def __init__(self, config): + + # Make fetching the overlay list a default action + if not 'nofetch' in config.keys(): + # Actions that implicitely call the fetch operation before + fetch_actions = ['fetch', 'sync', 'sync_all', 'list'] + for i in fetch_actions: + if i in config.keys(): + # Implicitely call fetch, break loop + Fetch(config).run() + break + + result = 0 + + for i in self.actions: + + OUT.debug('Checking for action', 7) + + if i[0] in config.keys(): + result += i[1](config).run() + + + if not result: + sys.exit(0) + else: + sys.exit(1) + +#=============================================================================== +# +# Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest, sys + + # Ignore warnings here. We are just testing + from warnings import filterwarnings, resetwarnings + filterwarnings('ignore') + + doctest.testmod(sys.modules[__name__]) + + resetwarnings() diff --git a/layman/config.py b/layman/config.py new file mode 100644 index 0000000..287f223 --- /dev/null +++ b/layman/config.py @@ -0,0 +1,300 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN CONFIGURATION +################################################################################# +# File: config.py +# +# Handles layman configuration +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +'''Defines the configuration options and provides parsing functionality.''' + +__version__ = "$Id: config.py 286 2007-01-09 17:48:23Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import sys, ConfigParser + +from optparse import OptionParser, OptionGroup +from layman.debug import OUT +from layman.version import VERSION + +#=============================================================================== +# +# Class Config +# +#------------------------------------------------------------------------------- + +class Config(object): + '''Handles the configuration.''' + + def __init__(self): + ''' + Creates and describes all possible polymeraZe options and creates + a debugging object. + + >>> import os.path + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> sys.argv.append('--config') + >>> sys.argv.append(here + '/../etc/layman.cfg') + >>> a = Config() + >>> a['overlays'] + '\\nhttp://www.gentoo.org/proj/en/overlays/layman-global.txt' + >>> sorted(a.keys()) + ['cache', 'config', 'config_dir', 'local_list', 'make_conf', 'nocheck', 'overlays', 'proxy', 'quietness', 'storage'] + ''' + + self.defaults = {'config_dir': '/etc/layman', + 'config' : '/etc/layman/layman.cfg', + 'storage' : '/usr/portage/local/layman', + 'cache' : '%(storage)s/cache', + 'local_list': '%(storage)s/overlays.xml', + 'make_conf' : '%(storage)s/make.conf', + 'nocheck' : 'no', + 'proxy' : '', + 'overlays' : + 'http://www.gentoo.org/proj/en/overlays/layman-global.' + 'txt',} + + + self.parser = OptionParser( + usage = '\n\nlayman -a/-d/-S|overlay\nlayman -f [-o url]\nlayman' \ + ' -l|-L', + version = VERSION) + + #----------------------------------------------------------------- + # Main Options + + group = OptionGroup(self.parser, + '') + + group.add_option('-a', + '--add', + action = 'append', + help = 'Add the given overlay from the cached remote li' + 'st to your locally installed overlays.. Specify "ALL" ' + 'to add all overlays from the remote list.') + + group.add_option('-d', + '--delete', + action = 'append', + help = 'Remove the given overlay from your locally inst' + 'alled overlays. Specify "ALL" to remove all overlays') + + group.add_option('-s', + '--sync', + action = 'append', + help = 'Update the specified overlay. Use "ALL" as para' + 'meter to synchronize all overlays') + + group.add_option('-S', + '--sync-all', + action = 'store_true', + help = 'Update all overlays.') + + group.add_option('-L', + '--list', + action = 'store_true', + help = 'List the contents of the remote list.') + + group.add_option('-l', + '--list-local', + action = 'store_true', + help = 'List the locally installed overlays.') + + group.add_option('-f', + '--fetch', + action = 'store_true', + help = 'Fetch a remote list of overlays. This option is' + ' deprecated. The fetch operation will be performed by ' + 'default when you run sync, sync-all, or list.') + + group.add_option('-n', + '--nofetch', + action = 'store_true', + help = 'Do not fetch a remote list of overlays.') + + group.add_option('-p', + '--priority', + action = 'store', + help = 'Use this with the --add switch to set the prior' + 'ity of the added overlay. This will influence the sort' + 'ing order of the overlays in the PORTDIR_OVERLAY varia' + 'ble.') + + self.parser.add_option_group(group) + + #----------------------------------------------------------------- + # Additional Options + + group = OptionGroup(self.parser, + '') + + group.add_option('-c', + '--config', + action = 'store', + help = 'Path to the config file [default: ' \ + + self.defaults['config'] + '].') + + group.add_option('-o', + '--overlays', + action = 'append', + help = 'The list of overlays [default: ' \ + + self.defaults['overlays'] + '].') + + self.parser.add_option_group(group) + + #----------------------------------------------------------------- + # Output Options + + group = OptionGroup(self.parser, + '') + + group.add_option('-v', + '--verbose', + action = 'store_true', + help = 'Increase amount of output.') + + group.add_option('-q', + '--quiet', + action = 'store_true', + help = 'Yield no output. Please be careful with this op' + 'tion: If the processes spawned by layman when adding o' + 'r synchronizing overlays require any input layman will' + ' hang without telling you why. This might happen for e' + 'xample if your overlay resides in subversion and the S' + 'SL certificate of the server needs acceptance.') + + group.add_option('-Q', + '--quietness', + action = 'store', + type = 'int', + default = '4', + help = 'Set the level of output (0-4). Default: 4. Once' + ' you set this below 2 the same warning as given for --' + 'quiet applies! ') + + group.add_option('-k', + '--nocheck', + action = 'store_true', + help = 'Do not check overlay definitions and do not i' + 'ssue a warning if description or contact information' + ' are missing.') + + self.parser.add_option_group(group) + + #----------------------------------------------------------------- + # Debug Options + + OUT.cli_opts(self.parser) + + # Parse the command line first since we need to get the config + # file option. + (self.options, args) = self.parser.parse_args() + + # handle debugging + OUT.cli_handle(self.options) + + # Fetch only an alternate config setting from the options + if not self.options.__dict__['config'] is None: + self.defaults['config'] = self.options.__dict__['config'] + + OUT.debug('Got config file at ' + self.defaults['config'], 8) + + # Now parse the config file + self.config = ConfigParser.ConfigParser(self.defaults) + self.config.add_section('MAIN') + + # handle quietness + if self['quiet']: + OUT.set_info_level(1) + OUT.set_warn_level(1) + self.defaults['quietness'] = 0 + elif 'quietness' in self.keys(): + OUT.set_info_level(int(self['quietness'])) + OUT.set_warn_level(int(self['quietness'])) + + OUT.debug('Reading config file at ' + self.defaults['config'], 8) + + self.config.read(self.defaults['config']) + + def __getitem__(self, key): + + if key == 'overlays': + overlays = '' + if (key in self.options.__dict__.keys() + and not self.options.__dict__[key] is None): + overlays = '\n'.join(self.options.__dict__[key]) + if self.config.has_option('MAIN', 'overlays'): + overlays += '\n' + self.config.get('MAIN', 'overlays') + if overlays: + return overlays + + OUT.debug('Retrieving option', 8) + + if (key in self.options.__dict__.keys() + and not self.options.__dict__[key] is None): + return self.options.__dict__[key] + + OUT.debug('Retrieving option', 8) + + if self.config.has_option('MAIN', key): + if key == 'nocheck': + if self.config.get('MAIN', key).lower() == 'yes': + return True + else: + return False + return self.config.get('MAIN', key) + + OUT.debug('Retrieving option', 8) + + if key in self.defaults.keys(): + return self.defaults[key] + + OUT.debug('Retrieving option', 8) + + return None + + def keys(self): + '''Special handler for the configuration keys.''' + + OUT.debug('Retrieving keys', 8) + + keys = [i for i in self.options.__dict__.keys() + if not self.options.__dict__[i] is None] + + OUT.debug('Retrieving keys', 8) + + keys += [name for name, value in self.config.items('MAIN') + if not name in keys] + + OUT.debug('Retrieving keys', 8) + + keys += [i for i in self.defaults.keys() + if not i in keys] + + OUT.debug('Retrieving keys', 8) + + return keys + + +#=============================================================================== +# +# Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest + doctest.testmod(sys.modules[__name__]) diff --git a/layman/db.py b/layman/db.py new file mode 100644 index 0000000..ae32114 --- /dev/null +++ b/layman/db.py @@ -0,0 +1,585 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN OVERLAY DB +################################################################################# +# File: db.py +# +# Access to the db of overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +'''Handles different storage files.''' + +__version__ = "$Id: db.py 309 2007-04-09 16:23:38Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import os, os.path, urllib2, re, md5 + +from layman.utils import path +from layman.overlay import Overlays + +from layman.debug import OUT + +#=============================================================================== +# +# Class DB +# +#------------------------------------------------------------------------------- + +class DB(Overlays): + ''' Handle the list of local overlays.''' + + def __init__(self, config): + + self.config = config + + self.path = config['local_list'] + + if config['nocheck']: + ignore = 2 + else: + ignore = 1 + + quiet = int(config['quietness']) < 3 + + Overlays.__init__(self, + [config['local_list'], ], + ignore, + quiet) + + OUT.debug('DB handler initiated', 6) + + def add(self, overlay): + ''' + Add an overlay to the local list of overlays. + + >>> write = os.tmpnam() + >>> write2 = os.tmpnam() + >>> write3 = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : write2, + ... 'nocheck' : True, + ... 'storage' : write3, + ... 'quietness':3} + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> a = DB(config) + >>> config['local_list'] = write + >>> b = DB(config) + >>> OUT.color_off() + + >>> m = MakeConf(config, b.overlays) + >>> m.path = write2 + >>> m.write() + + Commented out since it needs network access: + + # >>> b.add(a.select('wrobel-stable')) #doctest: +ELLIPSIS + # * Running command "/usr/bin/rsync -rlptDvz --progress --delete --delete-after --timeout=180 --exclude="distfiles/*" --exclude="local/*" --exclude="packages/*" "rsync://gunnarwrobel.de/wrobel-stable/*" "/tmp/file.../wrobel-stable""... + # >>> c = Overlays([write, ]) + # >>> c.overlays.keys() + # [u'wrobel-stable'] + + # >>> m = MakeConf(config, b.overlays) + # >>> [i.name for i in m.overlays] #doctest: +ELLIPSIS + # [u'wrobel-stable'] + + + # >>> os.unlink(write) + >>> os.unlink(write2) + >>> import shutil + + # >>> shutil.rmtree(write3) + ''' + + if overlay.name not in self.overlays.keys(): + result = overlay.add(self.config['storage']) + if result == 0: + if 'priority' in self.config.keys(): + overlay.priority = int(self.config['priority']) + self.overlays[overlay.name] = overlay + self.write(self.path) + make_conf = MakeConf(self.config, self.overlays) + make_conf.add(overlay) + else: + overlay.delete(self.config['storage']) + raise Exception('Adding the overlay failed!') + else: + raise Exception('Overlay "' + overlay.name + '" already in the loca' + 'l list!') + + def delete(self, overlay): + ''' + Add an overlay to the local list of overlays. + + >>> write = os.tmpnam() + >>> write2 = os.tmpnam() + >>> write3 = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : write2, + ... 'nocheck' : True, + ... 'storage' : write3, + ... 'quietness':3} + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> a = DB(config) + >>> config['local_list'] = write + >>> b = DB(config) + >>> OUT.color_off() + + >>> m = MakeConf(config, b.overlays) + >>> m.path = here + '/tests/testfiles/make.conf' + >>> m.read() + + >>> m.path = write2 + >>> m.write() + + # >>> b.add(a.select('wrobel-stable')) #doctest: +ELLIPSIS + # * Running command "/usr/bin/rsync -rlptDvz --progress --delete --delete-after --timeout=180 --exclude="distfiles/*" --exclude="local/*" --exclude="packages/*" "rsync://gunnarwrobel.de/wrobel-stable/*" "/tmp/file.../wrobel-stable""... + # >>> b.add(a.select('wrobel')) #doctest: +ELLIPSIS + # * Running command "/usr/bin/svn co "https://overlays.gentoo.org/svn/dev/wrobel/" "/tmp/file.../wrobel""... + # >>> c = Overlays([write, ]) + # >>> c.overlays.keys() + # [u'wrobel', u'wrobel-stable'] + + # >>> b.delete(b.select('wrobel')) + # >>> c = Overlays([write, ]) + # >>> c.overlays.keys() + # [u'wrobel-stable'] + + # >>> m = MakeConf(config, b.overlays) + # >>> [i.name for i in m.overlays] #doctest: +ELLIPSIS + # [u'wrobel-stable'] + + # >>> os.unlink(write) + >>> os.unlink(write2) + >>> import shutil + + # >>> shutil.rmtree(write3) + ''' + + if overlay.name in self.overlays.keys(): + make_conf = MakeConf(self.config, self.overlays) + overlay.delete(self.config['storage']) + del self.overlays[overlay.name] + self.write(self.path) + make_conf.delete(overlay) + else: + raise Exception('No local overlay named "' + overlay.name + '"!') + + def sync(self, overlay_name): + '''Synchronize the given overlay.''' + + overlay = self.select(overlay_name) + + if overlay: + result = overlay.sync(self.config['storage']) + if result: + raise Exception('Syncing overlay "' + overlay_name + + '" returned status ' + str(result) + '!') + else: + raise Exception('No such overlay ("' + overlay_name + '")!') + +#=============================================================================== +# +# Class RemoteDB +# +#------------------------------------------------------------------------------- + +class RemoteDB(Overlays): + '''Handles fetching the remote overlay list.''' + + def __init__(self, config): + + self.config = config + + self.proxies = {} + + if config['proxy']: + self.proxies['http'] = config['proxy'] + elif os.getenv('http_proxy'): + self.proxies['http'] = os.getenv('http_proxy') + + if self.proxies: + proxy_handler = urllib2.ProxyHandler(self.proxies) + opener = urllib2.build_opener(proxy_handler) + urllib2.install_opener(opener) + + self.urls = [i.strip() for i in config['overlays'].split('\n') if i] + + paths = [self.path(i) for i in self.urls] + + if config['nocheck']: + ignore = 2 + else: + ignore = 0 + + quiet = int(config['quietness']) < 3 + + Overlays.__init__(self, paths, ignore, quiet) + + def cache(self): + ''' + Copy the remote overlay list to the local cache. + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> cache = os.tmpnam() + >>> config = {'overlays' : + ... 'file://' + here + '/tests/testfiles/global-overlays.xml', + ... 'cache' : cache, + ... 'nocheck' : True, + ... 'proxy' : None, + ... 'quietness':3} + >>> a = RemoteDB(config) + >>> a.cache() + >>> b = open(a.path(config['overlays'])) + >>> b.readlines()[24] + ' A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org].\\n' + + >>> b.close() + >>> os.unlink(a.path(config['overlays'])) + + >>> a.overlays.keys() + [u'wrobel', u'wrobel-stable'] + ''' + for url in self.urls: + + mpath = self.path(url) + + try: + + # Fetch the remote list + olist = urllib2.urlopen(url).read() + + # Create our storage directory if it is missing + if not os.path.exists(os.path.dirname(mpath)): + try: + os.makedirs(os.path.dirname(mpath)) + except OSError, error: + raise OSError('Failed to create layman storage direct' + + 'ory ' + os.path.dirname(mpath) + '\n' + + 'Error was:' + str(error)) + + # Before we overwrite the old cache, check that the downloaded + # file is intact and can be parsed + try: + self.read(olist) + except Exception, error: + raise IOError('Failed to parse the overlays list fetched fr' + 'om ' + url + '\nThis means that the download' + 'ed file is somehow corrupt or there was a pr' + 'oblem with the webserver. Check the content ' + 'of the file. Error was:\n' + str(error)) + + # Ok, now we can overwrite the old cache + try: + out_file = open(mpath, 'w') + out_file.write(olist) + out_file.close() + + except Exception, error: + raise IOError('Failed to temporarily cache overlays list in' + ' ' + mpath + '\nError was:\n' + str(error)) + + + except IOError, error: + OUT.warn('Failed to update the overlay list from: ' + + url + '\nError was:\n' + str(error)) + + try: + # Finally parse the contents of the cache + self.read_file(mpath) + except IOError, error: + OUT.warn('Failed to read a cached version of the overlay list f' + 'rom ' + url + '. You probably did not download the fi' + 'le before. The corresponding entry in your layman.cfg' + ' file will be disregarded.\nError was:\n' + str(error) + ) + + def path(self, url): + '''Return a unique file name for the url.''' + + base = self.config['cache'] + + OUT.debug('Generating cache path.', 6) + + return base + '_' + md5.md5(url).hexdigest() + '.xml' + +#=============================================================================== +# +# Helper class MakeConf +# +#------------------------------------------------------------------------------- + +class MakeConf: + ''' + Handles modifications to /etc/make.conf + + Check that an add/remove cycle does not modify the make.conf: + + >>> import md5 + >>> write = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : here + '/tests/testfiles/make.conf', + ... 'nocheck' : True, + ... 'storage' : '/usr/portage/local/layman', + ... 'quietness':3} + >>> b = DB(config) + >>> a = MakeConf(config, b.overlays) + >>> o_md5 = str(md5.md5(open(here + '/tests/testfiles/make.conf').read()).hexdigest()) + >>> a.path = write + >>> a.add(b.overlays['wrobel-stable']) + >>> [i.name for i in a.overlays] + [u'wrobel-stable', u'wrobel-stable'] + >>> a.add(b.overlays['wrobel']) + >>> [i.name for i in a.overlays] + [u'wrobel', u'wrobel-stable', u'wrobel-stable'] + >>> a.delete(b.overlays['wrobel-stable']) + >>> [i.name for i in a.overlays] + [u'wrobel'] + >>> a.add(b.overlays['wrobel-stable']) + >>> [i.name for i in a.overlays] + [u'wrobel', u'wrobel-stable'] + >>> a.delete(b.overlays['wrobel']) + >>> n_md5 = str(md5.md5(open(write).read()).hexdigest()) + >>> o_md5 == n_md5 + True + >>> os.unlink(write) + ''' + + my_re = re.compile('PORTDIR_OVERLAY\s*=\s*"([^"]*)"') + + def __init__(self, config, overlays): + + self.path = config['make_conf'] + self.storage = config['storage'] + self.data = '' + self.db = overlays + self.overlays = [] + self.extra = [] + + self.read() + + def add(self, overlay): + ''' + Add an overlay to make.conf. + + >>> write = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : here + '/tests/testfiles/make.conf', + ... 'nocheck' : True, + ... 'storage' : '/usr/portage/local/layman', + ... 'quietness':3} + >>> c = DB(config) + >>> a = MakeConf(config, c.overlays) + >>> a.path = write + >>> a.add(c.select('wrobel')) + >>> config['make_conf'] = write + >>> b = MakeConf(config, c.overlays) + >>> [i.name for i in b.overlays] + [u'wrobel', u'wrobel-stable'] + >>> b.extra + ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + + >>> os.unlink(write) + ''' + self.overlays.append(overlay) + self.write() + + def delete(self, overlay): + ''' + Delete an overlay from make.conf. + + >>> write = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : here + '/tests/testfiles/make.conf', + ... 'nocheck' : True, + ... 'storage' : '/usr/portage/local/layman', + ... 'quietness':3} + >>> c = DB(config) + >>> a = MakeConf(config, c.overlays) + >>> a.path = write + >>> a.delete(c.select('wrobel-stable')) + >>> config['make_conf'] = write + >>> b = MakeConf(config, c.overlays) + >>> [i.name for i in b.overlays] + [] + >>> b.extra + ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + + >>> os.unlink(write) + ''' + self.overlays = [i + for i in self.overlays + if i.name != overlay.name] + self.write() + + def read(self): + ''' + Read the list of registered overlays from /etc/make.conf. + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : here + '/tests/testfiles/make.conf', + ... 'nocheck' : True, + ... 'storage' : '/usr/portage/local/layman', + ... 'quietness':3} + >>> c = DB(config) + >>> a = MakeConf(config, c.overlays) + >>> [i.name for i in a.overlays] + [u'wrobel-stable'] + >>> a.extra + ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + ''' + if os.path.isfile(self.path): + self.content() + + overlays = self.my_re.search(self.data) + + if not overlays: + raise Exception('Did not find a PORTDIR_OVERLAY entry in file ' + + self.path +'! Did you specify the correct file?') + + overlays = [i.strip() + for i in overlays.group(1).split('\n') + if i.strip()] + + for i in overlays: + if i[:len(self.storage)] == self.storage: + oname = os.path.basename(i) + if oname in self.db.keys(): + self.overlays.append(self.db[oname]) + else: + # These are additional overlays that we dont know + # anything about. The user probably added them manually + self.extra.append(i) + else: + # These are additional overlays that we dont know anything + # about. The user probably added them manually + self.extra.append(i) + + + else: + self.overlays = [] + self.data = 'PORTDIR_OVERLAY="\n"\n' + + self.extra = [i for i in self.extra + if (i != '$PORTDIR_OVERLAY' + and i != '${PORTDIR_OVERLAY}')] + + def write(self): + ''' + Write the list of registered overlays to /etc/make.conf. + + >>> write = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> config = {'local_list' : + ... here + '/tests/testfiles/global-overlays.xml', + ... 'make_conf' : here + '/tests/testfiles/make.conf', + ... 'nocheck' : True, + ... 'storage' : '/usr/portage/local/layman', + ... 'quietness':3} + >>> c = DB(config) + >>> a = MakeConf(config, c.overlays) + >>> a.path = write + >>> a.write() + >>> config['make_conf'] = write + >>> b = MakeConf(config, c.overlays) + >>> [i.name for i in b.overlays] + [u'wrobel-stable'] + >>> b.extra + ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + + >>> os.unlink(write) + ''' + def prio_sort(a, b): + '''Sort by priority.''' + if a.priority < b.priority: + return -1 + elif a.priority > b.priority: + return 1 + return 0 + + self.overlays.sort(prio_sort) + + paths = [] + for i in self.overlays: + paths.append(path((self.storage, i.name, ))) + + overlays = 'PORTDIR_OVERLAY="\n' + overlays += '\n'.join(paths) + '\n' + overlays += '$PORTDIR_OVERLAY\n' + overlays += '\n'.join(self.extra) + overlays += '"' + + content = self.my_re.sub(overlays, self.data) + + if not self.my_re.search(content): + raise Exception('Ups, failed to set a proper PORTDIR_OVERLAY entry ' + 'in file ' + self.path +'! Did not overwrite the fi' + 'le.') + + try: + make_conf = open(self.path, 'w') + + make_conf.write(content) + + make_conf.close() + + except Exception, error: + raise Exception('Failed to read "' + self.path + '".\nError was:\n' + + str(error)) + + def content(self): + ''' + Returns the content of the /etc/make.conf file. + ''' + try: + make_conf = open(self.path) + + self.data = make_conf.read() + + make_conf.close() + + except Exception, error: + raise Exception('Failed to read "' + self.path + '".\nError was:\n' + + str(error)) + +#=============================================================================== +# +# Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest, sys + + # Ignore warnings here. We are just testing + from warnings import filterwarnings, resetwarnings + filterwarnings('ignore') + + doctest.testmod(sys.modules[__name__]) + + resetwarnings() diff --git a/layman/debug.py b/layman/debug.py new file mode 100644 index 0000000..925f49c --- /dev/null +++ b/layman/debug.py @@ -0,0 +1,501 @@ +################################################################################# +# LAYMAN - DEBUGGING FUNCTIONS +################################################################################# +# debug.py -- Utility function for debugging +# Copyright 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 + +__version__ = "$Id: debug.py 153 2006-06-05 06:03:16Z wrobel $" + +################################################################################# +## +## Dependancies +## +################################################################################# + +import sys, inspect + +from optparse import OptionGroup + +################################################################################# +## +## Color codes (taken from portage) +## +################################################################################# + +esc_seq = '\x1b[' + +codes = {} +codes['reset'] = esc_seq + '39;49;00m' +codes['red'] = esc_seq + '31;01m' +codes['green'] = esc_seq + '32;01m' +codes['yellow'] = esc_seq + '33;01m' +codes['turquoise'] = esc_seq + '36;01m' + +################################################################################# +## +## Message Class +## +################################################################################# + +class Message: + #FIXME: Think about some simple doctests before you modify this class the + # next time. + + def __init__(self, module = '', + err = sys.stderr, + dbg = sys.stderr, + debugging_level = 4, + debugging_verbosity = 2, + info_level = 4, + warn_level = 4, + col = True, + mth = ['*'], + obj = ['*'], + var = ['*']): + + # A description of the module that is being debugged + self.debug_env = module + + # Where should the debugging output go? This can also be a file + self.debug_out = dbg + + # Where should the error output go? This can also be a file + self.error_out = err + + # The higher the level the more information you will get + self.warn_lev = warn_level + + # The higher the level the more information you will get + self.info_lev = info_level + + # The highest level of debugging messages acceptable for output + # The higher the level the more output you will get + self.debug_lev = debugging_level + + # The debugging output can range from very verbose (3) to + # very compressed (1) + self.debug_vrb = debugging_verbosity + + # Which methods should actually be debugged? + # Use '*' to indicate 'All methods' + self.debug_mth = mth + + # Which objects should actually be debugged? + # Use '*' to indicate 'All objects' + self.debug_obj = obj + + # Which variables should actually be debugged? + # Use '*' to indicate 'All variables' + self.debug_var = var + + # Exclude class variables by default + self.show_class_variables = False + + # Should the output be colored? + self.use_color = col + + self.has_error = False + + + ############################################################################ + # Add command line options + + def cli_opts(self, parser): + + group = OptionGroup(parser, + '', + 'Control the debugging features of ' + + self.debug_env) + + group.add_option('--debug', + action = 'store_true', + help = 'Activates debugging features.') + + group.add_option('--debug-level', + action = 'store', + type = 'int', + help = 'A value between 0 and 10. 0 means no debugging ' + 'messages will be selected, 10 selects all debugging me' + 'ssages. Default is "4".') + + group.add_option('--debug-verbose', + action = 'store', + type = 'int', + help = 'A value between 1 and 3. Lower values yield les' + 's verbose debugging output. Default is "2".') + + group.add_option('--debug-methods', + action = 'store', + help = 'Limits the methods that will return debugging o' + 'utput. The function name is sufficient and there is no' + 'difference between class methods or general functions.' + ' Several methods can be specified by seperating them w' + ' with a comma. Default is "*" which specifies all meth' + 'ods.') + + group.add_option('--debug-classes', + action = 'store', + help = 'Limits the classes that will return debugging o' + 'utput. Specify only the class name not including the m' + 'odules in which the class is defined (e.g. MyModule.ma' + 'in.Main should only be represented by "Main"). Several' + 'classes can be specified by seperating them with a com' + 'ma. Default is "*" which specifies all classes.') + + group.add_option('--debug-variables', + action = 'store', + help = 'Limits the variables that will return debugging' + ' output. Several variables can be specified by seperat' + 'ing them with a comma. Default is "*" which specifies ' + 'all variables.') + + group.add_option('--debug-class-vars', + action = 'store_true', + help = 'In default mode the debugging code will only re' + 'turn information on the local variable which does not ' + 'include the class variables. Use this switch to add al' + 'l values that are provided by "self".') + + group.add_option('--debug-nocolor', + action = 'store_true', + help = 'Deactivates colors in the debugging output.') + + parser.add_option_group(group) + + + ############################################################################# + # Handle command line options + + def cli_handle(self, options): + + if (options.__dict__.has_key('debug') + and options.__dict__['debug']): + self.debug_on() + else: + self.debug_off() + return + + if (options.__dict__.has_key('debug_class_vars') + and options.__dict__['debug_class_vars']): + self.class_variables_on() + else: + self.class_variables_off() + + if (options.__dict__.has_key('debug_nocolor') + and options.__dict__['debug_nocolor']): + self.color_off() + else: + self.color_on() + + if (options.__dict__.has_key('debug_level') and + options.__dict__['debug_level']): + dbglvl = int(options.__dict__['debug_level']) + if dbglvl < 0: + dbglvl = 0 + if dbglvl > 10: + dbglvl = 10 + self.set_debug_level(dbglvl) + + if (options.__dict__.has_key('debug_verbose') and + options.__dict__['debug_verbose']): + dbgvrb = int(options.__dict__['debug_verbose']) + if dbgvrb < 1: + dbgvrb = 1 + if dbgvrb > 3: + dbgvrb = 3 + self.set_debug_verbosity(dbgvrb) + + for i in [('debug_methods', self.set_debug_methods), + ('debug_classes', self.set_debug_classes), + ('debug_variables', self.set_debug_variables),]: + + if (options.__dict__.has_key(i[0]) and + options.__dict__[i[0]]): + i[1](options.__dict__[i[0]]) + + + ############################################################################# + ## Helper Functions + + def set_module(self, module): + + self.debug_env = module + + def set_debug_methods(self, methods): + + methods = methods.split(',') + + if methods: + self.debug_mth = methods + + def set_debug_classes(self, classes): + + classes = classes.split(',') + + if classes: + self.debug_obj = classes + + def set_debug_variables(self, variables): + + variables = variables.split(',') + + if variables: + self.debug_var = variables + + def maybe_color (self, col, text): + if self.use_color: + return codes[col] + text + codes['reset'] + return text + + def set_info_level(self, info_level = 4): + self.info_lev = info_level + + def info_off(self): + self.set_info_level(0) + + def info_on(self, info_level = 4): + self.set_info_level(info_level) + + def set_warn_level(self, warn_level = 4): + self.warn_lev = warn_level + + def warn_off(self): + self.set_warn_level(0) + + def warn_on(self, warn_level = 4): + self.set_warn_level(warn_level) + + def set_debug_level(self, debugging_level = 4): + self.debug_lev = debugging_level + + def set_debug_verbosity(self, debugging_verbosity = 2): + self.debug_vrb = debugging_verbosity + + def debug_off(self): + self.set_debug_level(0) + + def debug_on(self): + self.set_debug_level() + + def color_off(self): + self.use_color = False + + def color_on(self): + self.use_color = True + + def class_variables_off(self): + self.show_class_variables = False + + def class_variables_on(self): + self.show_class_variables = True + + ############################################################################# + ## Output Functions + + def notice (self, note): + print note + + def info (self, info, level = 4): + + info = str(info) + + if level > self.info_lev: + return + + for i in info.split('\n'): + print self.maybe_color('green', '* ') + i + + def status (self, message, status, info = 'ignored'): + + message = str(message) + + lines = message.split('\n') + + if not lines: + return + + for i in lines[0:-1]: + print self.maybe_color('green', '* ') + i + + i = lines[-1] + + if len(i) > 58: + i = i[0:57] + + if status == 1: + result = '[' + self.maybe_color('green', 'ok') + ']' + elif status == 0: + result = '[' + self.maybe_color('red', 'failed') + ']' + else: + result = '[' + self.maybe_color('yellow', info) + ']' + + print self.maybe_color('green', '* ') + i + ' ' + '.' * (58 - len(i)) \ + + ' ' + result + + def warn (self, warn, level = 4): + + warn = str(warn) + + if level > self.warn_lev: + return + + for i in warn.split('\n'): + print self.maybe_color('yellow', '* ') + i + + def error (self, error): + + error = str(error) + + for i in error.split('\n'): + print >> self.error_out, self.maybe_color('red', '* ') + i + self.has_error = True + + def die (self, error): + + error = str(error) + + for i in error.split('\n'): + self.error(self.maybe_color('red', 'Fatal error: ') + i) + self.error(self.maybe_color('red', 'Fatal error(s) - aborting')) + sys.exit(1) + + def debug (self, message, level = 4): + ''' + This is a generic debugging method. + ''' + ## Check the debug level first. This is the most inexpensive check. + if level > self.debug_lev: + return + + ## Maybe this should be debugged. So get the stack first. + stack = inspect.stack() + + ## This can probably never happen but does not harm to check + ## that there is actually something calling this function + if len(stack) < 2: + return + + ## Get the stack length to determine indentation of the debugging output + stacklength = len(stack) + ls = ' ' * stacklength + + ## Get the information about the caller + caller = stack[1] + + ## The function name of the calling frame is the fourth item in the list + callermethod = caller[3] + + ## Is this actually one of the methods that should be debugged? + if not '*' in self.debug_mth and not callermethod in self.debug_mth: + return + + ## Still looks like this should be debugged. So retrieve the dictionary + ## of local variables from the caller + callerlocals = inspect.getargvalues(caller[0])[3] + + ## Is the caller an obejct? If so he provides 'self' + if 'self' in callerlocals.keys(): + callerobject = callerlocals['self'] + del callerlocals['self'] + if self.show_class_variables: + cv = inspect.getmembers(callerobject, + lambda x: not inspect.ismethod(x)) + callerlocals.sync(cv) + else: + callerobject = None + + # Remove variables not requested + if not '*' in self.debug_var: + callerlocals = dict([i for i in callerlocals.items() + if i[0] in self.debug_var]) + + ## Is the object among the list of objects to debug? + if (not '*' in self.debug_obj and + not str(callerobject.__class__.__name__) in self.debug_obj): + return + + message = str(message) + + def breaklines(x): + ''' + Helper function to keep width of the debugging output. + + This may look ugly for arrays but it is acceptable and not + breaking the line would break the output format + ''' + ## Get the number of lines we need (rounded down) + lines = len(x) // 60 + if lines > 0: + for j in range(lines): + ## Print line with continuation marker + print >> self.debug_out, ls + '// ' + x[0:60] + ' \\' + ## Remove printed characters from output + x = x[60:] + ## Print final line + print >> self.debug_out, ls + '// ' + x + + if self.debug_vrb == 1: + # Top line indicates class and method + c = '' + if callerobject: + c += 'Class: ' + str(callerobject.__class__.__name__) + ' | ' + if callermethod: + c += 'Method: ' + str(callermethod) + print >> self.debug_out, '// ' + c + # Selected variables follow + if callerlocals: + for i,j in callerlocals.items(): + print >> self.debug_out, '// ' \ + + self.maybe_color('turquoise', str(i)) + ':' + str(j) + # Finally the message + print >> self.debug_out, self.maybe_color('yellow', message) + return + + if self.debug_vrb == 3: + print >> self.debug_out, ls + '/////////////////////////////////' + \ + '////////////////////////////////' + + # General information about what is being debugged + #(module name or similar) + print >> self.debug_out, ls + '// ' + self.debug_env + print >> self.debug_out, ls + '//-----------------------------------' + \ + '----------------------------' + + ## If the caller is a class print the name here + if callerobject: + print >> self.debug_out, ls + \ + '// Object Class: ' + str(callerobject.__class__.__name__) + + ## If the method has been extracted print it here + if callermethod: + print >> self.debug_out, ls + '// ' \ + + self.maybe_color('green', 'Method: ') + str(callermethod) + if self.debug_vrb == 3: + print >> self.debug_out, ls + '//---------------------------' + \ + '------------------------------------' + + ## Print the information on all available local variables + if callerlocals: + if self.debug_vrb == 3: + print >> self.debug_out, ls + '//' + print >> self.debug_out, ls + '// VALUES ' + for i,j in callerlocals.items(): + print >> self.debug_out, ls + '// ------------------> ' \ + + self.maybe_color('turquoise', str(i)) + ':' + breaklines(str(j)) + if self.debug_vrb == 3: + print >> self.debug_out, ls + '//------------------------------'\ + '---------------------------------' + + # Finally print the message + breaklines(self.maybe_color('yellow', message)) + + if self.debug_vrb == 3: + print >> self.debug_out, ls + '//-------------------------------' + \ + '--------------------------------' + print >> self.debug_out, ls + '/////////////////////////////////' + \ + '////////////////////////////////' + +## gloabal message handler +OUT = Message('layman') diff --git a/layman/overlay.py b/layman/overlay.py new file mode 100644 index 0000000..38e9364 --- /dev/null +++ b/layman/overlay.py @@ -0,0 +1,247 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN OVERLAY HANDLER +################################################################################# +# File: overlay.py +# +# Access to an xml list of overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +'''Main handler for overlays.''' + +__version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import os, os.path, xml.dom.minidom + +from layman.overlays.bzr import BzrOverlay +from layman.overlays.darcs import DarcsOverlay +from layman.overlays.git import GitOverlay +from layman.overlays.mercurial import MercurialOverlay +from layman.overlays.cvs import CvsOverlay +from layman.overlays.svn import SvnOverlay +from layman.overlays.rsync import RsyncOverlay +from layman.overlays.tar import TarOverlay + +from layman.debug import OUT + +#=============================================================================== +# +# Constants +# +#------------------------------------------------------------------------------- + +OVERLAY_TYPES = {'git' : GitOverlay, + 'cvs' : CvsOverlay, + 'svn' : SvnOverlay, + 'rsync' : RsyncOverlay, + 'tar' : TarOverlay, + 'bzr' : BzrOverlay, + 'mercurial' : MercurialOverlay, + 'darcs' : DarcsOverlay} + +#=============================================================================== +# +# Class Overlays +# +#------------------------------------------------------------------------------- + +class Overlays: + ''' Handle a list of overlays.''' + + def __init__(self, paths, ignore = 0, quiet = False): + + self.quiet = quiet + self.paths = paths + self.ignore = ignore + + self.overlays = {} + + OUT.debug('Initializing overlay list handler', 8) + + for path in self.paths: + if os.path.exists(path): + self.read_file(path) + + def read_file(self, path): + '''Read the overlay definition file.''' + + try: + + document = open(path).read() + + except Exception, error: + raise IOError('Failed to read the overlay list at ("' + + path + '")!\nError was:\n' + str(error)) + + + self.read(document) + + def read(self, document): + ''' + Read an xml list of overlays. + + >>> here = os.path.dirname(os.path.realpath(__file__)) + + >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ]) + >>> a.overlays.keys() + [u'wrobel', u'wrobel-stable'] + + >>> a.overlays['wrobel-stable'].data['&src'] + u'rsync://gunnarwrobel.de/wrobel-stable' + ''' + try: + + document = xml.dom.minidom.parseString(document) + + except Exception, error: + raise Exception('Failed to parse the overlay list!\nError was:\n' + + str(error)) + + overlays = document.getElementsByTagName('overlay') + + for overlay in overlays: + + OUT.debug('Parsing overlay entry', 8) + + for index in range(0, overlay.attributes.length): + attr = overlay.attributes.item(index) + if attr.name == 'type': + if attr.nodeValue in OVERLAY_TYPES.keys(): + try: + ovl = OVERLAY_TYPES[attr.nodeValue](overlay, + self.ignore, + self.quiet) + self.overlays[ovl.name] = ovl + except Exception, error: + OUT.warn(str(error), 3) + else: + raise Exception('Unknown overlay type "' + + attr.nodeValue + '"!') + + def write(self, path): + ''' + Write the list of overlays to a file. + + >>> write = os.tmpnam() + >>> here = os.path.dirname(os.path.realpath(__file__)) + + >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ]) + >>> b = Overlays([write,]) + >>> b.overlays['wrobel-stable'] = a.overlays['wrobel-stable'] + >>> b.write(write) + >>> c = Overlays([write,]) + >>> c.overlays.keys() + [u'wrobel-stable'] + + >>> os.unlink(write) + ''' + + imp = xml.dom.minidom.getDOMImplementation() + + doc = imp.createDocument('layman', 'overlays', None) + + root = doc.childNodes[0] + + for name, overlay in self.overlays.items(): + + root.appendChild(overlay.to_minidom(doc)) + + try: + + out_file = open(path, 'w') + + doc.writexml(out_file, '', ' ', '\n') + + except Exception, error: + raise Exception('Failed to write to local overlays file: ' + + path + '\nError was:\n' + str(error)) + + def select(self, overlay): + ''' + Select an overlay from the list. + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ]) + >>> a.select('wrobel-stable').data['&src'] + u'rsync://gunnarwrobel.de/wrobel-stable' + ''' + + if overlay in self.overlays.keys(): + return self.overlays[overlay] + + def list(self, verbose = False): + ''' + List all overlays. + + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> a = Overlays([here + '/tests/testfiles/global-overlays.xml', ]) + >>> for i in a.list(True): + ... print i[0] + wrobel + ~~~~~~ + Source : https://overlays.gentoo.org/svn/dev/wrobel + Contact : nobody@gentoo.org + Type : Subversion; Priority: 10 + + Description: + Test + + wrobel-stable + ~~~~~~~~~~~~~ + Source : rsync://gunnarwrobel.de/wrobel-stable + Contact : nobody@gentoo.org + Type : Rsync; Priority: 50 + + Description: + A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org]. + + + >>> for i in a.list(False): + ... print i[0] + wrobel [Subversion] (source: https://overlays.gentoo.or...) + wrobel-stable [Rsync ] (source: rsync://gunnarwrobel.de/wr...) + ''' + result = [] + + for name, overlay in self.overlays.items(): + + if verbose: + result.append((str(overlay), overlay.is_supported(), + overlay.is_official())) + else: + result.append((overlay.short_list(), overlay.is_supported(), + overlay.is_official())) + + result = sorted(result) + + return result + +#=============================================================================== +# +# Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest, sys + + # Ignore warnings here. We are just testing + from warnings import filterwarnings, resetwarnings + filterwarnings('ignore') + + doctest.testmod(sys.modules[__name__]) + + resetwarnings() diff --git a/layman/overlays/.svn.ignore b/layman/overlays/.svn.ignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/layman/overlays/.svn.ignore @@ -0,0 +1 @@ +*.pyc diff --git a/layman/overlays/__init__.py b/layman/overlays/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/layman/overlays/__init__.py @@ -0,0 +1 @@ +# diff --git a/layman/overlays/bzr.py b/layman/overlays/bzr.py new file mode 100644 index 0000000..8e8bb47 --- /dev/null +++ b/layman/overlays/bzr.py @@ -0,0 +1,66 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN BZR OVERLAY HANDLER +################################################################################# +# File: bzr.py +# +# Handles bzr overlays +# +# Copyright: +# (c) 2005 - 2006 Adrian Perez, Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Adrian Perez +# Gunnar Wrobel +# +'''Should work with any version of Bzr equal to or better than 0.7 -- + caution: tested only with 0.8 and 0.8.2...''' + +__version__ = "$Id: bzr.py 236 2006-09-05 20:39:37Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class BzrOverlay +# +#------------------------------------------------------------------------------- + +class BzrOverlay(Overlay): + ''' Handles bzr overlays.''' + + type = 'Bzr' + + binary_command = '/usr/bin/bzr' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + return self.cmd(self.binary_command + ' get "' + self.src + '/" "' +\ + path([base, self.name]) + '"') + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd('cd "' + path([base, self.name]) + '" && ' + \ + self.binary_command + ' pull --overwrite "' + self.src \ + + '"') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary_command, 'bzr', + 'dev-util/bzr'),]) diff --git a/layman/overlays/cvs.py b/layman/overlays/cvs.py new file mode 100644 index 0000000..95f20ea --- /dev/null +++ b/layman/overlays/cvs.py @@ -0,0 +1,73 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN CVS OVERLAY HANDLER +################################################################################# +# File: cvs.py +# +# Handles cvs overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Cvs overlay support.''' + +__version__ = "$Id$" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class CvsOverlay +# +#------------------------------------------------------------------------------- + +class CvsOverlay(Overlay): + ''' Handles cvs overlays.''' + + type = 'cvs' + + binary = '/usr/bin/cvs' + + def __init__(self, xml, ignore = 0, quiet = False): + + Overlay.__init__(self, xml, ignore, quiet) + + if '&subpath' in self.data.keys(): + self.subpath = self.data['&subpath'] + else: + self.subpath = '' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + return self.cmd('cd "' + base + '" && CVSROOT="' + self.src + '" ' + + self.binary + ' co -d "' + self.name + + '" "' + self.subpath + '"' ) + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd('cd "' + path([base, self.name]) + '" && ' + + self.binary + ' update') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary, 'cvs', + 'dev-util/cvs'),]) diff --git a/layman/overlays/darcs.py b/layman/overlays/darcs.py new file mode 100644 index 0000000..56e6d91 --- /dev/null +++ b/layman/overlays/darcs.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN DARCS OVERLAY HANDLER +################################################################################# +# File: darcs.py +# +# Handles darcs overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel, Andres Loeh +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# Andres Loeh +# +''' Darcs overlay support.''' + +__version__ = "$Id: darcs.py 236 2006-09-05 20:39:37Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class BzrOverlay +# +#------------------------------------------------------------------------------- + +class DarcsOverlay(Overlay): + ''' Handles darcs overlays.''' + + type = 'Darcs' + + binary_command = '/usr/bin/darcs' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + return self.cmd(self.binary_command + ' get --partial "' + self.src + + '/" "' + path([base, self.name]) + '"') + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd('cd "' + path([base, self.name]) + '" && ' + + self.binary_command + ' pull --all "' + self.src + '"') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary_command, 'darcs', + 'dev-util/darcs'),]) diff --git a/layman/overlays/git.py b/layman/overlays/git.py new file mode 100644 index 0000000..007e841 --- /dev/null +++ b/layman/overlays/git.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN GIT OVERLAY HANDLER +################################################################################# +# File: git.py +# +# Handles git overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel, Stefan Schweizer +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# Stefan Schweizer +''' Git overlay support.''' + +__version__ = "$Id: git.py 146 2006-05-27 09:52:36Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class GitOverlay +# +#------------------------------------------------------------------------------- + +class GitOverlay(Overlay): + ''' Handles git overlays.''' + + type = 'Git' + + binary_command = '/usr/bin/git' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + return self.cmd(self.binary_command + ' clone "' + self.src + '/" "' + + path([base, self.name]) + '"') + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd('cd "' + path([base, self.name]) + '" && ' + + self.binary_command + ' pull') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary_command, 'git', + 'dev-util/git'),]) diff --git a/layman/overlays/mercurial.py b/layman/overlays/mercurial.py new file mode 100644 index 0000000..3def5fc --- /dev/null +++ b/layman/overlays/mercurial.py @@ -0,0 +1,64 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN MERCURIAL OVERLAY HANDLER +################################################################################# +# File: darcs.py +# +# Handles darcs overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel, Andres Loeh +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# Andres Loeh +# +''' Mercurial overlay support.''' + +__version__ = "$Id: mercurial.py 236 2006-09-05 20:39:37Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class MercurialOverlay +# +#------------------------------------------------------------------------------- + +class MercurialOverlay(Overlay): + ''' Handles mercurial overlays.''' + + type = 'Mercurial' + + binary_command = '/usr/bin/hg' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + return self.cmd(self.binary_command + ' clone "' + self.src + '/" "' + + path([base, self.name]) + '"') + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd('cd "' + path([base, self.name]) + '" && ' + + self.binary_command + ' pull -u "' + self.src + '"') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary_command, 'mercurial', + 'dev-util/mercurial'),]) diff --git a/layman/overlays/overlay.py b/layman/overlays/overlay.py new file mode 100644 index 0000000..b7a006f --- /dev/null +++ b/layman/overlays/overlay.py @@ -0,0 +1,266 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN OVERLAY BASE CLASS +################################################################################# +# File: overlay.py +# +# Base class for the different overlay types. +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Basic overlay class.''' + +__version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import re, os, os.path, shutil, popen2 + +from layman.utils import node_to_dict, dict_to_node, path + +from layman.debug import OUT + +#=============================================================================== +# +# Class Overlay +# +#------------------------------------------------------------------------------- + +class Overlay: + ''' Derive the real implementations from this.''' + + type = 'None' + + def __init__(self, xml, ignore = 0, quiet = False): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read() + >>> import xml.dom.minidom + >>> document = xml.dom.minidom.parseString(document) + >>> overlays = document.getElementsByTagName('overlay') + >>> a = Overlay(overlays[0]) + >>> a.name + u'wrobel' + >>> a.is_official() + True + >>> a.src + u'https://overlays.gentoo.org/svn/dev/wrobel' + >>> a.contact + u'nobody@gentoo.org' + >>> a.description + u'Test' + >>> a.priority + 10 + >>> b = Overlay(overlays[1]) + >>> b.is_official() + False + ''' + self.quiet = quiet + + self.data = node_to_dict(xml) + + if '&name' in self.data.keys(): + self.name = self.data['&name'] + else: + raise Exception('Overlay is missing a "name" attribute!') + + if '&src' in self.data.keys(): + self.src = self.data['&src'] + else: + raise Exception('Overlay "' + self.name + '" is missing a "src" ' + 'attribute!') + + if '&contact' in self.data.keys(): + self.contact = self.data['&contact'] + else: + self.contact = '' + if not ignore: + raise Exception('Overlay "' + self.name + '" is missing a ' + '"contact" attribute!') + elif ignore == 1: + OUT.warn('Overlay "' + self.name + '" is missing a ' + '"contact" attribute!', 4) + + if '1' in self.data.keys(): + self.description = self.data['1']['@'].strip() + else: + self.description = '' + if not ignore: + raise Exception('Overlay "' + self.name + '" is missing a ' + '"description" entry!') + elif ignore == 1: + OUT.warn('Overlay "' + self.name + '" is missing a ' + '"description" entry!', 4) + + if '&status' in self.data.keys(): + self.status = self.data['&status'] + else: + self.status = '' + + if '&priority' in self.data.keys(): + self.priority = int(self.data['&priority']) + else: + self.priority = 50 + + def to_minidom(self, document): + '''Convert to xml.''' + + return dict_to_node(self.data, document, 'overlay') + + def add(self, base): + '''Add the overlay.''' + + mdir = path([base, self.name]) + + if os.path.exists(mdir): + raise Exception('Directory ' + mdir + ' already exists. Will not ov' + 'erwrite its contents!') + + os.makedirs(mdir) + + def sync(self, base): + '''Sync the overlay.''' + pass + + def delete(self, base): + '''Delete the overlay.''' + mdir = path([base, self.name]) + + if not os.path.exists(mdir): + raise Exception('Directory ' + mdir + ' does not exist. Cannot remo' + 've the overlay!') + + shutil.rmtree(mdir) + + def cmd(self, command): + '''Run a command.''' + + OUT.info('Running command "' + command + '"...', 2) + + if not self.quiet: + return os.system(command) + else: + cmd = popen2.Popen4(command) + cmd.fromchild.readlines() + result = cmd.wait() + cmd.fromchild.readlines() + cmd.fromchild.close() + cmd.tochild.close() + return result + + def __str__(self): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read() + >>> import xml.dom.minidom + >>> document = xml.dom.minidom.parseString(document) + >>> overlays = document.getElementsByTagName('overlay') + >>> a = Overlay(overlays[0]) + >>> print str(a) + wrobel + ~~~~~~ + Source : https://overlays.gentoo.org/svn/dev/wrobel + Contact : nobody@gentoo.org + Type : None; Priority: 10 + + Description: + Test + + ''' + + result = '' + + result += self.name + '\n' + (len(self.name) * '~') + + result += '\nSource : ' + self.src + result += '\nContact : ' + self.contact + result += '\nType : ' + self.type + result += '; Priority: ' + str(self.priority) + '\n' + + description = self.description + description = re.compile(' +').sub(' ', description) + description = re.compile('\n ').sub('\n', description) + result += '\nDescription:' + result += '\n '.join(('\n' + description).split('\n')) + result += '\n' + + if '1' in self.data.keys(): + link = self.data['1']['@'].strip() + link = re.compile(' +').sub(' ', link) + link = re.compile('\n ').sub('\n', link) + result += '\nLink:\n' + result += '\n '.join(('\n' + link).split('\n')) + result += '\n' + + return result + + def short_list(self): + ''' + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> document = open(here + '/../tests/testfiles/global-overlays.xml').read() + >>> import xml.dom.minidom + >>> document = xml.dom.minidom.parseString(document) + >>> overlays = document.getElementsByTagName('overlay') + >>> a = Overlay(overlays[0]) + >>> print a.short_list() + wrobel [None ] (source: https://overlays.gentoo.or...) + ''' + + def pad(string, length): + '''Pad a string with spaces.''' + if len(string) <= length: + return string + ' ' * (length - len(string)) + else: + return string[:length - 3] + '...' + + name = pad(self.name, 25) + mtype = ' [' + pad(self.type, 10) + ']' + source = ' (source: ' + pad(self.src, 29) + ')' + + return name + mtype + source + + def supported(self, binaries = []): + '''Is the overlay type supported?''' + + if binaries: + for mpath, mtype, package in binaries: + if not os.path.exists(mpath): + raise Exception('Binary ' + mpath + ' seems to be missing!' + ' Overlay type "' + mtype + '" not support' + 'ed. Did you emerge ' + package + '?') + + return True + + def is_supported(self): + '''Is the overlay type supported?''' + + try: + self.supported() + return True + except Exception, error: + return False + + def is_official(self): + '''Is the overlay official?''' + + return self.status == 'official' + +#================================================================================ +# +# Testing +# +#-------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest, sys + doctest.testmod(sys.modules[__name__]) diff --git a/layman/overlays/rsync.py b/layman/overlays/rsync.py new file mode 100644 index 0000000..e2483ad --- /dev/null +++ b/layman/overlays/rsync.py @@ -0,0 +1,68 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN RSYNC OVERLAY HANDLER +################################################################################# +# File: rsync.py +# +# Handles rsync overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Rsync overlay support.''' + +__version__ = "$Id: rsync.py 236 2006-09-05 20:39:37Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class RsyncOverlay +# +#------------------------------------------------------------------------------- + +class RsyncOverlay(Overlay): + ''' Handles rsync overlays.''' + + type = 'Rsync' + + binary = '/usr/bin/rsync' + + base = binary + ' -rlptDvz --progress --delete --delete-after ' + \ + '--timeout=180 --exclude="distfiles/*" --exclude="local/*" ' + \ + '--exclude="packages/*" ' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + Overlay.add(self, base) + + return self.sync(base) + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd(self.base + '"' + self.src + '/*" "' + + path([base, self.name]) + '"') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary, 'rsync', + 'net-misc/rsync'),]) diff --git a/layman/overlays/svn.py b/layman/overlays/svn.py new file mode 100644 index 0000000..5086448 --- /dev/null +++ b/layman/overlays/svn.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN SVN OVERLAY HANDLER +################################################################################# +# File: svn.py +# +# Handles subversion overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Subversion overlay support.''' + +__version__ = "$Id: svn.py 236 2006-09-05 20:39:37Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class SvnOverlay +# +#------------------------------------------------------------------------------- + +class SvnOverlay(Overlay): + ''' Handles subversion overlays.''' + + type = 'Subversion' + + binary = '/usr/bin/svn' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + Overlay.add(self, base) + + return self.cmd(self.binary + ' co "' + self.src + '/" "' + + path([base, self.name]) + '"') + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + return self.cmd(self.binary + ' update "' + path([base, self.name]) + + '"') + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary, 'svn', + 'dev-util/subversion'),]) diff --git a/layman/overlays/tar.py b/layman/overlays/tar.py new file mode 100644 index 0000000..24a9b91 --- /dev/null +++ b/layman/overlays/tar.py @@ -0,0 +1,189 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN TAR OVERLAY HANDLER +################################################################################# +# File: tar.py +# +# Handles tar overlays +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +''' Tar overlay support.''' + +__version__ = "$Id: tar.py 310 2007-04-09 16:30:40Z wrobel $" + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import os, os.path, sys, urllib2, shutil + +from layman.utils import path +from layman.overlays.overlay import Overlay + +#=============================================================================== +# +# Class TarOverlay +# +#------------------------------------------------------------------------------- + +class TarOverlay(Overlay): + ''' Handles tar overlays. + + A dummy tar handler that overwrites the __init__ method + so that we don't need to provide xml input: + + >>> from layman.debug import OUT + >>> class DummyTar(TarOverlay): + ... def __init__(self): + ... self.name = 'dummy' + ... here = os.path.dirname(os.path.realpath(__file__)) + ... self.src = 'file://' + here + '/../tests/testfiles/layman-test.tar.bz2' + ... self.subpath = 'layman-test' + ... self.format = 'bz2' + ... self.quiet = False + >>> testdir = os.tmpnam() + >>> os.mkdir(testdir) + >>> a = DummyTar() + >>> OUT.color_off() + >>> a.add(testdir) #doctest: +ELLIPSIS + * Running command "/bin/tar -v -x -j -f... + >>> sorted(os.listdir(testdir + '/dummy')) + ['app-admin', 'app-portage'] + >>> shutil.rmtree(testdir) + ''' + + type = 'Tar' + + binary = '/bin/tar' + + def __init__(self, xml, ignore = 0, quiet = False): + + Overlay.__init__(self, xml, ignore) + + if '&format' in self.data.keys(): + self.format = self.data['&format'] + else: + self.format = '' + + if '&subpath' in self.data.keys(): + self.subpath = self.data['&subpath'] + else: + self.subpath = '' + + if '&category' in self.data.keys(): + if self.subpath: + raise Exception('Cannot use "category" and "subpath" at the same' + ' time!') + + self.category = self.data['&category'] + else: + self.category = '' + + def add(self, base): + '''Add overlay.''' + + self.supported() + + mdir = path([base, self.name]) + + if os.path.exists(mdir): + raise Exception('Directory ' + mdir + ' already exists. Will not ov' + 'erwrite its contents!') + + if self.format == 'bz2' or (not self.format and self.src[-3:] == 'bz2'): + ext = 'bz2' + opt = '-j' + elif self.format == 'gz' or (not self.format and self.src[-2:] == 'gz'): + ext = 'gz' + opt = '-z' + else: + raise Exception('Unsupported file format!') + + try: + + tar = urllib2.urlopen(self.src).read() + + except Exception, error: + raise Exception('Failed to fetch the tar package from: ' + + self.src + '\nError was:' + str(error)) + + pkg = path([base, self.name + '.tar.' + ext]) + + try: + + out_file = open(pkg, 'w') + out_file.write(tar) + out_file.close() + + except Exception, error: + raise Exception('Failed to store tar package in ' + + pkg + '\nError was:' + str(error)) + + if self.subpath: + target = path([base, 'tmp']) + else: + if self.category: + target = mdir + '/' + self.category + else: + target = mdir + + os.makedirs(target) + + result = self.cmd(self.binary + ' -v -x ' + opt + ' -f "' + pkg + + '" -C "' + target + '"') + + if self.subpath: + source = target + '/' + self.subpath + if os.path.exists(source): + try: + os.rename(source, mdir) + except Exception, error: + raise Exception('Failed to rename tar subdirectory ' + + source + ' to ' + mdir + '\nError was:' + + str(error)) + else: + raise Exception('Given subpath "' + source + '" does not exist ' + ' in the tar package!') + try: + shutil.rmtree(target) + except Exception, error: + raise Exception('Failed to remove unnecessary tar structure "' + + target + '"\nError was:' + str(error)) + + os.unlink(pkg) + + return result + + def sync(self, base): + '''Sync overlay.''' + + self.supported() + + self.delete(base) + + self.add(base) + + def supported(self): + '''Overlay type supported?''' + + return Overlay.supported(self, [(self.binary, 'tar', 'app-arch/tar'), ]) + +if __name__ == '__main__': + import doctest + + # Ignore warnings here. We are just testing + from warnings import filterwarnings, resetwarnings + filterwarnings('ignore') + + doctest.testmod(sys.modules[__name__]) + + resetwarnings() diff --git a/layman/tests/dtest.py b/layman/tests/dtest.py new file mode 100644 index 0000000..8f65af1 --- /dev/null +++ b/layman/tests/dtest.py @@ -0,0 +1,90 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN DOCTEST AGGREGATOR +################################################################################# +# File: dtest.py +# +# Combines the doctests that are available for the different modules +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# +'''Aggregates doctests from all modules that provide such tests.''' + +__version__ = '$Id: dtest.py 237 2006-09-05 21:18:54Z wrobel $' + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import unittest, doctest, sys + +# On module creation: + +# 1.) Check header section (copyright notice) +# 2.) Add module doc string +# 3.) Add version string +# 4.) Add testing handler at bottom of module +# 5.) Add module into tests/dtest.py. Check that tests run through +# 6.) Run pylint over the code. Fix any reasonable complaints. +# 7.) Whitespace clean the buffer. +# 8.) Add svn:keywords "Id" to file. + +# On module change: + +# 1.) Check header section (copyright notice) +# 5.) Check that tests run through +# 6.) Run pylint over the code. Fix any reasonable complaints. +# 7.) Whitespace clean the buffer. + +# clean modules : CT +# not yet clean : UT +# clean but no testing : CN +# unclean but no testing: UN + +import layman.action #CT +import layman.config #CT +import layman.db #CT +import layman.overlay #CT +import layman.utils #CT +import layman.overlays.overlay #CT +import layman.overlays.tar #CT + +#=============================================================================== +# +# Test Suite +# +#------------------------------------------------------------------------------- + +def test_suite(): + return unittest.TestSuite(( + doctest.DocTestSuite(layman.action), + doctest.DocTestSuite(layman.config), + doctest.DocTestSuite(layman.db), + doctest.DocTestSuite(layman.overlay), + doctest.DocTestSuite(layman.utils), + doctest.DocTestSuite(layman.overlays.overlay), + doctest.DocTestSuite(layman.overlays.tar), + )) + +#=============================================================================== +# +# Run Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + # Ignore warnings here. We are just testing + from warnings import filterwarnings, resetwarnings + filterwarnings('ignore') + + unittest.main(defaultTest='test_suite') + + resetwarnings() diff --git a/layman/tests/testfiles/global-overlays.xml b/layman/tests/testfiles/global-overlays.xml new file mode 100644 index 0000000..d770692 --- /dev/null +++ b/layman/tests/testfiles/global-overlays.xml @@ -0,0 +1,30 @@ + + + + + + + Test + + + + + + + + A collection of ebuilds from Gunnar Wrobel [wrobel@gentoo.org]. + + + + + diff --git a/layman/tests/testfiles/layman-test.tar.bz2 b/layman/tests/testfiles/layman-test.tar.bz2 new file mode 100644 index 0000000..85ee7fd Binary files /dev/null and b/layman/tests/testfiles/layman-test.tar.bz2 differ diff --git a/layman/tests/testfiles/make.conf b/layman/tests/testfiles/make.conf new file mode 100644 index 0000000..d32dd16 --- /dev/null +++ b/layman/tests/testfiles/make.conf @@ -0,0 +1,345 @@ +# Copyright 1999-2004 Gentoo Foundation +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-src/portage/cnf/make.conf.x86,v 1.5.2.5 2005/04/13 15:28:38 jstubbs Exp $ +# Contains local system settings for Portage system + +# Please review 'man make.conf' for more information. + +# Build-time functionality +# ======================== +# +# The USE variable is used to enable optional build-time functionality. For +# example, quite a few packages have optional X, gtk or GNOME functionality +# that can only be enabled or disabled at compile-time. Gentoo Linux has a +# very extensive set of USE variables described in our USE variable HOWTO at +# http://www.gentoo.org/doc/en/handbook/handbook-x86.xml?part=2&chap=1 +# +# The available list of use flags with descriptions is in your portage tree. +# Use 'less' to view them: --> less /usr/portage/profiles/use.desc <-- +# +# 'ufed' is an ncurses/dialog interface available in portage to make handling +# useflags for you. 'emerge app-portage/ufed' +# +# Example: + +# Use flags will be handled by polymeraZe +USE="-*" + +# Host Setting +# ============ +# +# DO NOT CHANGE THIS SETTING UNLESS YOU ARE USING STAGE1! +# Change this line as appropriate (i686, i586, i486 or i386). +# All modern systems (even Athlons) should use "i686-pc-linux-gnu". +# All K6's are i586. +CHOST="i686-pc-linux-gnu" + +# Host and optimization settings +# ============================== +# +# For optimal performance, enable a CFLAGS setting appropriate for your CPU. +# +# Please note that if you experience strange issues with a package, it may be +# due to gcc's optimizations interacting in a strange way. Please test the +# package (and in some cases the libraries it uses) at default optimizations +# before reporting errors to developers. +# +# -mcpu= means optimize code for the particular type of CPU without +# breaking compatibility with other CPUs. +# +# -march= means to take full advantage of the ABI and instructions +# for the particular CPU; this will break compatibility with older CPUs (for +# example, -march=athlon-xp code will not run on a regular Athlon, and +# -march=i686 code will not run on a Pentium Classic. +# +# CPU types supported in gcc-3.2 and higher: athlon-xp, athlon-mp, +# athlon-tbird, athlon, k6, k6-2, k6-3, i386, i486, i586 (Pentium), i686 +# (PentiumPro), pentium, pentium-mmx, pentiumpro, pentium2 (Celeron), +# pentium3, and pentium4. +# +# Note that Gentoo Linux 1.4 and higher include at least gcc-3.2. +# +# CPU types supported in gcc-2.95*: k6, i386, i486, i586 (Pentium), i686 +# (Pentium Pro), pentium, pentiumpro Gentoo Linux 1.2 and below use gcc-2.95* +# +# CRITICAL WARNINGS: ****************************************************** # +# K6 markings are deceptive. Avoid setting -march for them. See Bug #24379. # +# Pentium-M CPU's should not enable sse2 until at least gcc-3.4. Bug 50616. # +# ************************************************************************* # +# +# Decent examples: +# +#CFLAGS="-mcpu=athlon-xp -O3 -pipe" + +CFLAGS="-march=athlon-xp -O3 -pipe" + + +# If you set a CFLAGS above, then this line will set your default C++ flags to +# the same settings. +CXXFLAGS="${CFLAGS}" + +# Advanced Masking +# ================ +# +# Gentoo is using a new masking system to allow for easier stability testing +# on packages. KEYWORDS are used in ebuilds to mask and unmask packages based +# on the platform they are set for. A special form has been added that +# indicates packages and revisions that are expected to work, but have not yet +# been approved for the stable set. '~arch' is a superset of 'arch' which +# includes the unstable, in testing, packages. Users of the 'x86' architecture +# would add '~x86' to ACCEPT_KEYWORDS to enable unstable/testing packages. +# '~ppc', '~sparc' are the unstable KEYWORDS for their respective platforms. +# +# Please note that this is not for development, alpha, beta, nor cvs release +# packages. "Broken" packages will not be added to testing and should not be +# requested to be added. Alternative routes are available to developers +# for experimental packages, and it is at their discretion to use them. +# +# DO NOT PUT ANYTHING BUT YOUR SPECIFIC ~ARCHITECTURE IN THE LIST. +# IF YOU ARE UNSURE OF YOUR ARCH, OR THE IMPLICATIONS, DO NOT MODIFY THIS. +# + +ACCEPT_KEYWORDS="x86" + + +# Portage Directories +# =================== +# +# Each of these settings controls an aspect of portage's storage and file +# system usage. If you change any of these, be sure it is available when +# you try to use portage. *** DO NOT INCLUDE A TRAILING "/" *** +# +# PORTAGE_TMPDIR is the location portage will use for compilations and +# temporary storage of data. This can get VERY large depending upon +# the application being installed. +PORTAGE_TMPDIR=/var/tmp +# +# PORTDIR is the location of the portage tree. This is the repository +# for all profile information as well as all ebuilds. If you change +# this, you must update your /etc/make.profile symlink accordingly. +PORTDIR=/usr/portage +# +# DISTDIR is where all of the source code tarballs will be placed for +# emerges. The source code is maintained here unless you delete +# it. The entire repository of tarballs for gentoo is 9G. This is +# considerably more than any user will ever download. 2-3G is +# a large DISTDIR. +DISTDIR=/usr/distfiles +# +# PKGDIR is the location of binary packages that you can have created +# with '--buildpkg' or '-b' while emerging a package. This can get +# upto several hundred megs, or even a few gigs. +#PKGDIR=${PORTDIR}/packages +# +# PORT_LOGDIR is the location where portage will store all the logs it +# creates from each individual merge. They are stored as NNNN-$PF.log +# in the directory specified. This is disabled until you enable it by +# providing a directory. Permissions will be modified as needed IF the +# directory exists, otherwise logging will be disabled. NNNN is the +# increment at the time the log is created. Logs are thus sequential. +PORT_LOGDIR=/var/log/services/portage.d +# +# PORTDIR_OVERLAY is a directory where local ebuilds may be stored without +# concern that they will be deleted by rsync updates. Default is not +# defined. +PORTDIR_OVERLAY=" +/usr/portage/local/layman/wrobel-stable +$PORTDIR_OVERLAY +/usr/portage/local/ebuilds/testing +/usr/portage/local/ebuilds/stable +/usr/portage/local/kolab2 +/usr/portage/local/gentoo-webapps-overlay/experimental +/usr/portage/local/gentoo-webapps-overlay/production-ready" + +# Fetching files +# ============== +# +# If you need to set a proxy for wget or lukemftp, add the appropriate "export +# ftp_proxy=" and "export http_proxy=" lines to /etc/profile if +# all users on your system should use them. +# +# Portage uses wget by default. Here are some settings for some alternate +# downloaders -- note that you need to merge these programs first before they +# will be available. +# +# Default fetch command (5 tries, passive ftp for firewall compatibility) +#FETCHCOMMAND="/usr/bin/wget -t 5 --passive-ftp \${URI} -P \${DISTDIR}" +#RESUMECOMMAND="/usr/bin/wget -c -t 5 --passive-ftp \${URI} -P \${DISTDIR}" +# +# Using wget, ratelimiting downloads +#FETCHCOMMAND="/usr/bin/wget -t 5 --passive-ftp --limit-rate=200k \${URI} -P \${DISTDIR}" +#RESUMECOMMAND="/usr/bin/wget -c -t 5 --passive-ftp --limit-rate=200k \${URI} -P \${DISTDIR}" +# +# Lukemftp (BSD ftp): +#FETCHCOMMAND="/usr/bin/lukemftp -s -a -o \${DISTDIR}/\${FILE} \${URI}" +#RESUMECOMMAND="/usr/bin/lukemftp -s -a -R -o \${DISTDIR}/\${FILE} \${URI}" +# + +FETCHCOMMAND="/usr/bin/getdelta.sh \${URI}" + + +# Portage uses GENTOO_MIRRORS to specify mirrors to use for source retrieval. +# The list is a space separated list which is read left to right. If you use +# another mirror we highly recommend leaving the default mirror at the end of +# the list so that portage will fall back to it if the files cannot be found +# on your specified mirror. We _HIGHLY_ recommend that you change this setting +# to a nearby mirror by merging and using the 'mirrorselect' tool. + +GENTOO_MIRRORS="http://pandemonium.tiscali.de/pub/gentoo/ ftp://pandemonium.tiscali.de/pub/gentoo/ ftp://ftp-stud.fht-esslingen.de/pub/Mirrors/gentoo/ http://mir.zyrianes.net/gentoo/ http://ftp.snt.utwente.nl/pub/os/linux/gentoo http://distfiles.gentoo.org http://www.ibiblio.org/pub/Linux/distributions/gentoo" + +# +# Portage uses PORTAGE_BINHOST to specify mirrors for prebuilt-binary packages. +# The list is a single entry specifying the full address of the directory +# serving the tbz2's for your system. Running emerge with either '--getbinpkg' +# or '--getbinpkgonly' will cause portage to retrieve the metadata from all +# packages in the directory specified, and use that data to determine what will +# be downloaded and merged. '-g' or '-gK' are the recommend parameters. Please +# consult the man pages and 'emerge --help' for more information. For FTP, the +# default connection is passive -- If you require an active connection, affix +# an asterisk (*) to the end of the host:port string before the path. +#PORTAGE_BINHOST="http://grp.mirror.site/gentoo/grp/1.4/i686/athlon-xp/" +# This ftp connection is passive ftp. +#PORTAGE_BINHOST="ftp://login:pass@grp.mirror.site/pub/grp/i686/athlon-xp/" +# This ftp connection is active ftp. +#PORTAGE_BINHOST="ftp://login:pass@grp.mirror.site:21*/pub/grp/i686/athlon-xp/" + +# Synchronizing Portage +# ===================== +# +# Each of these settings affects how Gentoo synchronizes your Portage tree. +# Synchronization is handled by rsync and these settings allow some control +# over how it is done. +# +# +# SYNC is the server used by rsync to retrieve a localized rsync mirror +# rotation. This allows you to select servers that are geographically +# close to you, yet still distribute the load over a number of servers. +# Please do not single out specific rsync mirrors. Doing so places undue +# stress on particular mirrors. Instead you may use one of the following +# continent specific rotations: +# +# Default: "rsync://rsync.gentoo.org/gentoo-portage" +# North America: "rsync://rsync.namerica.gentoo.org/gentoo-portage" +# South America: "rsync://rsync.samerica.gentoo.org/gentoo-portage" +# Europe: "rsync://rsync.europe.gentoo.org/gentoo-portage" +# Asia: "rsync://rsync.asia.gentoo.org/gentoo-portage" +# Australia: "rsync://rsync.au.gentoo.org/gentoo-portage" + +SYNC="rsync://rsync.europe.gentoo.org/gentoo-portage" + +# +# RSYNC_RETRIES sets the number of times portage will attempt to retrieve +# a current portage tree before it exits with an error. This allows +# for a more successful retrieval without user intervention most times. +#RSYNC_RETRIES="3" +# +# RSYNC_TIMEOUT sets the length of time rsync will wait before it times out +# on a connection. Most users will benefit from this setting as it will +# reduce the amount of 'dead air' they experience when they run across +# the occasional, unreachable mirror. Dialup users might want to set this +# value up around the 300 second mark. +#RSYNC_TIMEOUT=180 + +# Advanced Features +# ================= +# +# MAKEOPTS provides extra options that may be passed to 'make' when a +# program is compiled. Presently the only use is for specifying +# the number of parallel makes (-j) to perform. The suggested number +# for parallel makes is CPUs+1. +MAKEOPTS="-j2" +# +# PORTAGE_NICENESS provides a default increment to emerge's niceness level. +# Note: This is an increment. Running emerge in a niced environment will +# reduce it further. Default is unset. +PORTAGE_NICENESS=3 +# +# AUTOCLEAN enables portage to automatically clean out older or overlapping +# packages from the system after every successful merge. This is the +# same as running 'emerge -c' after every merge. Set with: "yes" or "no". +# This does not affect the unpacked source. See 'noclean' below. +AUTOCLEAN="yes" +# +# PORTAGE_TMPFS is a location where portage may create temporary files. +# If specified, portage will use this directory whenever possible +# for all rapid operations such as lockfiles and transient data. +# It is _highly_ recommended that this be a tmpfs or ramdisk. Do not +# set this to anything that does not give a significant performance +# enhancement and proper FS compliance for locks and read/write. +# /dev/shm is a glibc mandated tmpfs, and should be a reasonable +# setting for all linux kernel+glibc based systems. +#PORTAGE_TMPFS="/dev/shm" +# +# FEATURES are settings that affect the functionality of portage. Most of +# these settings are for developer use, but some are available to non- +# developers as well. +# +# 'autoaddcvs' causes portage to automatically try to add files to cvs +# that will have to be added later. Done at generation times +# and only has an effect when 'cvs' is also set. +# 'buildpkg' causes binary packages to be created of all packages that +# are being merged. +# 'ccache' enables ccache support via CC. +# 'collision-protect' +# prevents packages from overwriting files that are owned by +# another package or by no package at all. +# 'cvs' causes portage to enable all cvs features (commits, adds), +# and to apply all USE flags in SRC_URI for digests -- for +# developers only. +# 'digest' causes digests to be generated for all packages being merged. +# 'distcc' enables distcc support via CC. +# 'distlocks' enables distfiles locking using fcntl or hardlinks. This +# is enabled by default. Tools exist to help clean the locks +# after crashes: /usr/lib/portage/bin/clean_locks. +# 'fixpackages' allows portage to fix binary packages that are stored in +# PKGDIR. This can consume a lot of time. 'fixpackages' is +# also a script that can be run at any given time to force +# the same actions. +# 'gpg' enables basic verification of Manifest files using gpg. +# This features is UNDER DEVELOPMENT and reacts to features +# of strict and severe. Heavy use of gpg sigs is coming. +# 'keeptemp' prevents the clean phase from deleting the temp files ($T) +# from a merge. +# 'keepwork' prevents the clean phase from deleting the WORKDIR. +# 'maketest' causes ebuilds to perform testing phases if they are capable +# of it. Some packages support this automaticaly via makefiles. +# 'noauto' causes ebuild to perform only the action requested and +# not any other required actions like clean or unpack -- for +# debugging purposes only. +# 'noclean' prevents portage from removing the source and temporary files +# after a merge -- for debugging purposes only. +# 'nostrip' prevents the stripping of binaries. +# 'notitles' disables xterm titlebar updates (which contain status info). +# 'sandbox' enables sandboxing when running emerge and ebuild. +# 'strict' causes portage to react strongly to conditions that are +# potentially dangerous, like missing/incorrect Manifest files. +# 'userpriv' allows portage to drop root privileges while it is compiling, +# as a security measure. As a side effect this can remove +# sandbox access violations for users. +# 'usersandbox' enables sandboxing while portage is running under userpriv. +#FEATURES="sandbox buildpkg ccache distcc userpriv usersandbox notitles noclean noauto cvs keeptemp keepwork autoaddcvs" +FEATURES="sandbox ccache userprivs distlocks cvs" +# +# CCACHE_SIZE sets the space use limitations for ccache. The default size is +# 2G, and will be set if not defined otherwise and ccache is in features. +# Portage will set the default ccache dir if it is not present in the +# user's environment, for userpriv it sets: ${PORTAGE_TMPDIR}/ccache +# (/var/tmp/ccache), and for regular use the default is /root/.ccache. +# Sizes are specified with 'G' 'M' or 'K'. +# '2G' for 2 gigabytes, '2048M' for 2048 megabytes (same as 2G). +CCACHE_SIZE="1G" +# +# DISTCC_DIR sets the temporary space used by distcc. +#DISTCC_DIR="${PORTAGE_TMPDIR}/.distcc" +# +# RSYNC_EXCLUDEFROM is a file that portage will pass to rsync when it updates +# the portage tree. Specific chunks of the tree may be excluded from +# consideration. This may cause dependency failures if you are not careful. +# The file format is one pattern per line, blanks and ';' or '#' lines are +# comments. See 'man rsync' for more details on the exclude-from format. +#RSYNC_EXCLUDEFROM=/etc/portage/rsync_excludes +EBEEP_IGNORE=yes + +CONFIG_PROTECT_MASK="/usr/X11R6/lib/X11" + + diff --git a/layman/utils.py b/layman/utils.py new file mode 100644 index 0000000..239f1b3 --- /dev/null +++ b/layman/utils.py @@ -0,0 +1,208 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# POLYMERAZE XML UTILITIES +################################################################################# +# File: xml.py +# +# Utilities to deal with xml nodes. +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# + +'''Utility functions to deal with xml nodes. ''' + +__version__ = '$Id: utils.py 236 2006-09-05 20:39:37Z wrobel $' + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- + +import types, re + +#=============================================================================== +# +# Helper functions +# +#------------------------------------------------------------------------------- + +def node_to_text(node): + ''' + Reduces an xml node to its text elements. The function does not + collect the text nodes recursively. + + >>> import xml.dom.minidom + >>> imp = xml.dom.minidom.getDOMImplementation() + >>> doc = imp.createDocument('test', 'root', None) + >>> root = doc.childNodes[0] + >>> node = doc.createTextNode('text') + >>> a = root.appendChild(node) + >>> node = doc.createElement('text') + >>> node2 = doc.createTextNode('text') + >>> a = node.appendChild(node2) + >>> a = root.appendChild(node) + >>> node = doc.createTextNode('text') + >>> a = root.appendChild(node) + >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS + '...texttexttext' + + >>> node_to_text(root) + 'texttext' + + ''' + text = '' + + for child in node.childNodes: + if child.nodeType == child.TEXT_NODE: + text = text + child.data + + return text + +def node_to_dict(node): + ''' Converts a xml node to a dictionary. The function collects the + nodes recursively. Attributes will be prepended with '&', child + nodes will be surrounded with tags. An index will be appended + since several child nodes with the same tag may exist. Text + elements will be collapsed and stored in a n entry prepended with + '@'. Comments will be ignored. + + >>> import xml.dom.minidom + >>> imp = xml.dom.minidom.getDOMImplementation() + >>> doc = imp.createDocument('test', 'root', None) + >>> root = doc.childNodes[0] + >>> node = doc.createTextNode('text') + >>> a = root.appendChild(node) + >>> node = doc.createElement('text') + >>> node2 = doc.createTextNode('text') + >>> comm = doc.createComment('comment') + >>> attr = doc.createAttribute('&attr') + >>> a = node.appendChild(node2) + >>> a = root.appendChild(comm) + >>> node.setAttributeNode(attr) + >>> node.setAttribute('&attr','test') + >>> a = root.appendChild(node) + >>> node3 = doc.createElement('text') + >>> a = root.appendChild(node3) + >>> node = doc.createTextNode('text') + >>> a = root.appendChild(node) + >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS + '...texttexttext' + + >>> node_to_dict(root) + {'1': {'@': 'text', '&&attr': 'test'}, '2': {'@': ''}, '@': 'texttext'} + + ''' + result = {} + + # Map the attributes + for index in range(0, node.attributes.length): + attr = node.attributes.item(index) + result['&' + attr.name] = attr.nodeValue + + text = '' + + # Map the nodes + for child in node.childNodes: + if child.nodeType == child.TEXT_NODE: + text = text + child.data + if child.nodeType == child.ELEMENT_NODE: + index = 1 + while ('<' + child.tagName + '>' + str(index)) in result.keys(): + index += 1 + result['<' + child.tagName + '>' + str(index)] = node_to_dict(child) + + result['@'] = text + + return result + +def dict_to_node(data, document, root_name): + ''' Reverts the node_to_dict operation. + + >>> import xml.dom.minidom + >>> imp = xml.dom.minidom.getDOMImplementation() + >>> doc = imp.createDocument('test', 'root', None) + >>> a = {'1': {'@': 'text', '&&attr': 'test'}, '2': {'@': ''}, '@': 'texttext'} + >>> doc.childNodes[0] = dict_to_node(a, doc, 'root') + >>> doc.toprettyxml('', '') #doctest: +ELLIPSIS + '...texttexttext' + + ''' + node = document.createElement(root_name) + + for i, j in data.items(): + + if i[0] == '&': + attr = document.createAttribute(i[1:]) + node.setAttributeNode(attr) + node.setAttribute(i[1:], j) + if i[0] == '<': + k = i[1:] + while k[-1] in '0123456789': + k = k[:-1] + child = dict_to_node(data[i], + document, + k[:-1]) + node.appendChild(child) + if i[0] == '@': + child = document.createTextNode(j) + node.appendChild(child) + + return node + +def path(path_elements): + ''' + Concatenate a path from several elements. + + >>> path([]) + '' + >>> path(['a']) + 'a' + >>> path(['a','b']) + 'a/b' + >>> path(['a/','b']) + 'a/b' + >>> path(['/a/','b']) + '/a/b' + >>> path(['/a/','b/']) + '/a/b' + >>> path(['/a/','b/']) + '/a/b' + >>> path(['/a/','/b/']) + '/a/b' + >>> path(['/a/','/b','c/']) + '/a/b/c' + ''' + pathname = '' + + if type(path_elements) in types.StringTypes: + path_elements = [path_elements] + + # Concatenate elements and seperate with / + for i in path_elements: + pathname += i + '/' + + # Replace multiple consecutive slashes + pathname = re.compile('/+').sub('/', pathname) + + # Remove the final / if there is one + if pathname and pathname[-1] == '/': + pathname = pathname[:-1] + + return pathname + +#=============================================================================== +# +# Testing +# +#------------------------------------------------------------------------------- + +if __name__ == '__main__': + import doctest, sys + doctest.testmod(sys.modules[__name__]) diff --git a/layman/version.py b/layman/version.py new file mode 100644 index 0000000..943522b --- /dev/null +++ b/layman/version.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +################################################################################# +# LAYMAN VERSION +################################################################################# +# File: version.py +# +# Current version number +# +# Copyright: +# (c) 2005 - 2006 Gunnar Wrobel +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Gunnar Wrobel +# + +__version__ = "$Id: version.py 309 2007-04-09 16:23:38Z wrobel $" + + +VERSION = '1.0.99' + +if __name__ == '__main__': + print VERSION diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..9c50c54 --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +import sys + +from distutils.core import setup + +# this affects the names of all the directories we do stuff with +sys.path.insert(0, './') +from layman.version import VERSION + + +setup(name = 'layman', + version = VERSION, + description = 'Python script for retrieving gentoo overlays', + author = 'Gunnar Wrobel', + author_email = 'wrobel@gentoo.org', + url = 'http://projects.gunnarwrobel.de/scripts', + packages = ['layman', 'layman.overlays'], + scripts = ['bin/layman'], + license = 'GPL', + ) -- cgit v1.2.3-1-g7c22 From c8d258ef16d97e51ecdba271658068ddf00b8a5f Mon Sep 17 00:00:00 2001 From: Gunnar Wrobel Date: Tue, 11 Sep 2007 09:34:48 +0000 Subject: Layman v1.1 --- ChangeLog | 57 +++++++++++++++++++ doc/layman.8.xml | 38 +++++++++++-- layman/action.py | 76 ++++++++++++++++++++++++++ layman/config.py | 19 ++++++- layman/db.py | 16 +++--- layman/debug.py | 20 ++++--- layman/overlay.py | 5 +- layman/overlays/overlay.py | 44 +++++++++------ layman/overlays/rsync.py | 2 +- layman/overlays/tar.py | 6 +- layman/tests/testfiles/overlays_bug_184449.xml | 19 +++++++ layman/version.py | 2 +- 12 files changed, 258 insertions(+), 46 deletions(-) create mode 100644 layman/tests/testfiles/overlays_bug_184449.xml diff --git a/ChangeLog b/ChangeLog index ec01be0..e6df33a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,60 @@ +2007-09-11 Gunnar Wrobel + + * layman/config.py (Config.__init__): + * doc/layman.8.xml: + + Add "nocolor" option (#183364) + http://bugs.gentoo.org/show_bug.cgi?id=183364 + + * layman/db.py: + * layman/debug.py: + + Fixes for unicode bug #184449 + http://bugs.gentoo.org/show_bug.cgi?id=184449 + + * layman/overlays/rsync.py (RsyncOverlay.sync): + + Fix bug #177045 + http://bugs.gentoo.org/show_bug.cgi?id=177045 + + * doc/layman.8.xml: + + Improve the man page concerning the --overlays flag (#180107) + http://bugs.gentoo.org/show_bug.cgi?id=180107 + + * layman/db.py (DB.add): + * layman/overlays/overlay.py (Overlay.set_priority): + + Correctly handle the priority setting (#185142) + http://bugs.gentoo.org/show_bug.cgi?id=185142 + + * layman/config.py (Config.__init__): + + Finally fix the default for "nocheck" + + * layman/version.py: + + Update to version 1.1. + + * layman/config.py (Config.__init__): + * layman/action.py (Info.run): + * doc/layman.8.xml: + + Add the new "--info" option (bug #188000) + http://bugs.gentoo.org/show_bug.cgi?id=188000 + + * doc/layman.8.xml: + + Description of the verbose switch (partial fix + for #188004). + http://bugs.gentoo.org/show_bug.cgi?id=188004 + + * layman/config.py (Config.__init__): + + Add the output options into the list of documented + cli switches (partial fix for #188004). + http://bugs.gentoo.org/show_bug.cgi?id=188004 + 2007-01-09 Gunnar Wrobel * layman/version.py: diff --git a/doc/layman.8.xml b/doc/layman.8.xml index c966786..ecd2ec7 100644 --- a/doc/layman.8.xml +++ b/doc/layman.8.xml @@ -419,6 +419,14 @@ + + overlay + overlay + + Display all available information about the specified overlay. + + + @@ -468,10 +476,13 @@ Specifies the location of additional overlay lists. You can use this flag several times and the - specified urls will get appended to the list of urls you - specified in your config file. You may also specify - local file urls by prepending the path with - file:// + specified urls will get temporarily appended to the list + of urls you specified in your config file. You may also + specify local file urls by prepending the path with + file://. This option will only append the URL for this + specific layman run - edit your config file to add a URL + permanently. So this is useful for testing purposes. + @@ -513,6 +524,25 @@ + + + + + Makes layman more verbose and + you will receive a description of the overlays you can + download. + + + + + + + + Remove color codes from the layman + output. + + + LEVEL LEVEL diff --git a/layman/action.py b/layman/action.py index b2c67ce..b8259a5 100644 --- a/layman/action.py +++ b/layman/action.py @@ -92,6 +92,9 @@ class Sync: self.selection = config['sync'] + enc = sys.getfilesystemencoding() + self.selection = [i.decode(enc) for i in self.selection] + if config['sync_all'] or 'ALL' in self.selection: self.selection = self.db.overlays.keys() @@ -153,6 +156,9 @@ class Add: self.selection = config['add'] + enc = sys.getfilesystemencoding() + self.selection = [i.decode(enc) for i in self.selection] + if 'ALL' in self.selection: self.selection = self.rdb.overlays.keys() @@ -197,6 +203,9 @@ class Delete: self.selection = config['delete'] + enc = sys.getfilesystemencoding() + self.selection = [i.decode(enc) for i in self.selection] + if 'ALL' in self.selection: self.selection = self.db.overlays.keys() @@ -226,6 +235,72 @@ class Delete: return result +#=============================================================================== +# +# Class Info +# +#------------------------------------------------------------------------------- + +class Info: + ''' Print information about the specified overlays. + + >>> import os + >>> here = os.path.dirname(os.path.realpath(__file__)) + >>> cache = os.tmpnam() + >>> config = {'overlays' : + ... 'file://' + here + '/tests/testfiles/global-overlays.xml', + ... 'cache' : cache, + ... 'proxy' : None, + ... 'info' : ['wrobel'], + ... 'nocheck' : False, + ... 'verbose': False, + ... 'quietness':3} + >>> a = Info(config) + >>> a.rdb.cache() + >>> OUT.color_off() + >>> a.run() + * wrobel + * ~~~~~~ + * Source : https://overlays.gentoo.org/svn/dev/wrobel + * Contact : nobody@gentoo.org + * Type : Subversion; Priority: 10 + * + * Description: + * Test + * + 0 + ''' + + def __init__(self, config): + + OUT.debug('Creating RemoteDB handler', 6) + + self.rdb = RemoteDB(config) + self.config = config + + self.selection = config['info'] + + enc = sys.getfilesystemencoding() + self.selection = [i.decode(enc) for i in self.selection] + + if 'ALL' in self.selection: + self.selection = self.rdb.overlays.keys() + + def run(self): + ''' Print information about the selected overlays.''' + + for i in self.selection: + overlay = self.rdb.select(i) + # Is the overlay supported? + OUT.info(overlay.__str__(), 1) + if not overlay.is_official(): + OUT.warn('*** This is no official gentoo overlay ***\n', 1) + if not overlay.is_supported(): + OUT.error('*** You are lacking the necessary tools to install t' + 'his overlay ***\n') + + return 0 + #=============================================================================== # # Class List @@ -370,6 +445,7 @@ class Actions: actions = [('fetch', Fetch), ('add', Add), ('sync', Sync), + ('info', Info), ('sync_all', Sync), ('delete', Delete), ('list', List), diff --git a/layman/config.py b/layman/config.py index 287f223..d13806f 100644 --- a/layman/config.py +++ b/layman/config.py @@ -61,7 +61,7 @@ class Config(object): 'cache' : '%(storage)s/cache', 'local_list': '%(storage)s/overlays.xml', 'make_conf' : '%(storage)s/make.conf', - 'nocheck' : 'no', + 'nocheck' : 'yes', 'proxy' : '', 'overlays' : 'http://www.gentoo.org/proj/en/overlays/layman-global.' @@ -98,6 +98,12 @@ class Config(object): help = 'Update the specified overlay. Use "ALL" as para' 'meter to synchronize all overlays') + group.add_option('-i', + '--info', + action = 'append', + help = 'Display information about the specified overlay' + '.') + group.add_option('-S', '--sync-all', action = 'store_true', @@ -164,7 +170,8 @@ class Config(object): group.add_option('-v', '--verbose', action = 'store_true', - help = 'Increase amount of output.') + help = 'Increase the amount of output and describe the ' + 'overlays.') group.add_option('-q', '--quiet', @@ -176,6 +183,11 @@ class Config(object): 'xample if your overlay resides in subversion and the S' 'SL certificate of the server needs acceptance.') + group.add_option('-N', + '--nocolor', + action = 'store_true', + help = 'Remove color codes from the layman output.') + group.add_option('-Q', '--quietness', action = 'store', @@ -206,6 +218,9 @@ class Config(object): # handle debugging OUT.cli_handle(self.options) + if self.options.__dict__['nocolor']: + OUT.color_off() + # Fetch only an alternate config setting from the options if not self.options.__dict__['config'] is None: self.defaults['config'] = self.options.__dict__['config'] diff --git a/layman/db.py b/layman/db.py index ae32114..5019dfb 100644 --- a/layman/db.py +++ b/layman/db.py @@ -24,7 +24,7 @@ __version__ = "$Id: db.py 309 2007-04-09 16:23:38Z wrobel $" # #------------------------------------------------------------------------------- -import os, os.path, urllib2, re, md5 +import os, codecs, os.path, urllib2, re, md5 from layman.utils import path from layman.overlay import Overlays @@ -109,7 +109,7 @@ class DB(Overlays): result = overlay.add(self.config['storage']) if result == 0: if 'priority' in self.config.keys(): - overlay.priority = int(self.config['priority']) + overlay.set_priority(self.config['priority']) self.overlays[overlay.name] = overlay self.write(self.path) make_conf = MakeConf(self.config, self.overlays) @@ -398,7 +398,7 @@ class MakeConf: >>> [i.name for i in b.overlays] [u'wrobel', u'wrobel-stable'] >>> b.extra - ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + [u'/usr/portage/local/ebuilds/testing', u'/usr/portage/local/ebuilds/stable', u'/usr/portage/local/kolab2', u'/usr/portage/local/gentoo-webapps-overlay/experimental', u'/usr/portage/local/gentoo-webapps-overlay/production-ready'] >>> os.unlink(write) ''' @@ -426,7 +426,7 @@ class MakeConf: >>> [i.name for i in b.overlays] [] >>> b.extra - ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + [u'/usr/portage/local/ebuilds/testing', u'/usr/portage/local/ebuilds/stable', u'/usr/portage/local/kolab2', u'/usr/portage/local/gentoo-webapps-overlay/experimental', u'/usr/portage/local/gentoo-webapps-overlay/production-ready'] >>> os.unlink(write) ''' @@ -451,7 +451,7 @@ class MakeConf: >>> [i.name for i in a.overlays] [u'wrobel-stable'] >>> a.extra - ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + [u'/usr/portage/local/ebuilds/testing', u'/usr/portage/local/ebuilds/stable', u'/usr/portage/local/kolab2', u'/usr/portage/local/gentoo-webapps-overlay/experimental', u'/usr/portage/local/gentoo-webapps-overlay/production-ready'] ''' if os.path.isfile(self.path): self.content() @@ -510,7 +510,7 @@ class MakeConf: >>> [i.name for i in b.overlays] [u'wrobel-stable'] >>> b.extra - ['/usr/portage/local/ebuilds/testing', '/usr/portage/local/ebuilds/stable', '/usr/portage/local/kolab2', '/usr/portage/local/gentoo-webapps-overlay/experimental', '/usr/portage/local/gentoo-webapps-overlay/production-ready'] + [u'/usr/portage/local/ebuilds/testing', u'/usr/portage/local/ebuilds/stable', u'/usr/portage/local/kolab2', u'/usr/portage/local/gentoo-webapps-overlay/experimental', u'/usr/portage/local/gentoo-webapps-overlay/production-ready'] >>> os.unlink(write) ''' @@ -542,7 +542,7 @@ class MakeConf: 'le.') try: - make_conf = open(self.path, 'w') + make_conf = codecs.open(self.path, 'w', 'utf-8') make_conf.write(content) @@ -557,7 +557,7 @@ class MakeConf: Returns the content of the /etc/make.conf file. ''' try: - make_conf = open(self.path) + make_conf = codecs.open(self.path, 'r', 'utf-8') self.data = make_conf.read() diff --git a/layman/debug.py b/layman/debug.py index 925f49c..04682dc 100644 --- a/layman/debug.py +++ b/layman/debug.py @@ -13,7 +13,7 @@ __version__ = "$Id: debug.py 153 2006-06-05 06:03:16Z wrobel $" ## ################################################################################# -import sys, inspect +import sys, inspect, types from optparse import OptionGroup @@ -298,7 +298,8 @@ class Message: def info (self, info, level = 4): - info = str(info) + if type(info) not in types.StringTypes: + info = str(info) if level > self.info_lev: return @@ -308,7 +309,8 @@ class Message: def status (self, message, status, info = 'ignored'): - message = str(message) + if type(message) not in types.StringTypes: + message = str(message) lines = message.split('\n') @@ -335,7 +337,8 @@ class Message: def warn (self, warn, level = 4): - warn = str(warn) + if type(warn) not in types.StringTypes: + warn = str(warn) if level > self.warn_lev: return @@ -345,7 +348,8 @@ class Message: def error (self, error): - error = str(error) + if type(error) not in types.StringTypes: + error = str(error) for i in error.split('\n'): print >> self.error_out, self.maybe_color('red', '* ') + i @@ -353,7 +357,8 @@ class Message: def die (self, error): - error = str(error) + if type(error) not in types.StringTypes: + error = str(error) for i in error.split('\n'): self.error(self.maybe_color('red', 'Fatal error: ') + i) @@ -415,7 +420,8 @@ class Message: not str(callerobject.__class__.__name__) in self.debug_obj): return - message = str(message) + if type(message) not in types.StringTypes: + message = str(message) def breaklines(x): ''' diff --git a/layman/overlay.py b/layman/overlay.py index 38e9364..ffbf32f 100644 --- a/layman/overlay.py +++ b/layman/overlay.py @@ -24,7 +24,7 @@ __version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $" # #------------------------------------------------------------------------------- -import os, os.path, xml.dom.minidom +import sys, codecs, os, os.path, xml.dom.minidom from layman.overlays.bzr import BzrOverlay from layman.overlays.darcs import DarcsOverlay @@ -161,7 +161,7 @@ class Overlays: try: - out_file = open(path, 'w') + out_file = codecs.open(path, 'w', 'utf-8') doc.writexml(out_file, '', ' ', '\n') @@ -178,7 +178,6 @@ class Overlays: >>> a.select('wrobel-stable').data['&src'] u'rsync://gunnarwrobel.de/wrobel-stable' ''' - if overlay in self.overlays.keys(): return self.overlays[overlay] diff --git a/layman/overlays/overlay.py b/layman/overlays/overlay.py index b7a006f..14aaa94 100644 --- a/layman/overlays/overlay.py +++ b/layman/overlays/overlay.py @@ -24,7 +24,7 @@ __version__ = "$Id: overlay.py 273 2006-12-30 15:54:50Z wrobel $" # #------------------------------------------------------------------------------- -import re, os, os.path, shutil, popen2 +import sys, types, re, os, os.path, shutil, popen2 from layman.utils import node_to_dict, dict_to_node, path @@ -112,6 +112,12 @@ class Overlay: else: self.priority = 50 + def set_priority(self, priority): + '''Set the priority of this overlay.''' + + self.data['&priority'] = str(priority) + self.priority = int(priority) + def to_minidom(self, document): '''Convert to xml.''' @@ -147,6 +153,10 @@ class Overlay: OUT.info('Running command "' + command + '"...', 2) + if hasattr(sys.stdout,'encoding'): + command = command.encode(sys.stdout.encoding or + sys.getfilesystemencoding()) + if not self.quiet: return os.system(command) else: @@ -178,29 +188,29 @@ class Overlay: ''' - result = '' + result = u'' - result += self.name + '\n' + (len(self.name) * '~') + result += self.name + u'\n' + (len(self.name) * u'~') - result += '\nSource : ' + self.src - result += '\nContact : ' + self.contact - result += '\nType : ' + self.type - result += '; Priority: ' + str(self.priority) + '\n' + result += u'\nSource : ' + self.src + result += u'\nContact : ' + self.contact + result += u'\nType : ' + self.type + result += u'; Priority: ' + str(self.priority) + u'\n' description = self.description - description = re.compile(' +').sub(' ', description) - description = re.compile('\n ').sub('\n', description) - result += '\nDescription:' - result += '\n '.join(('\n' + description).split('\n')) - result += '\n' + description = re.compile(u' +').sub(u' ', description) + description = re.compile(u'\n ').sub(u'\n', description) + result += u'\nDescription:' + result += u'\n '.join((u'\n' + description).split(u'\n')) + result += u'\n' if '1' in self.data.keys(): link = self.data['1']['@'].strip() - link = re.compile(' +').sub(' ', link) - link = re.compile('\n ').sub('\n', link) - result += '\nLink:\n' - result += '\n '.join(('\n' + link).split('\n')) - result += '\n' + link = re.compile(u' +').sub(u' ', link) + link = re.compile(u'\n ').sub(u'\n', link) + result += u'\nLink:\n' + result += u'\n '.join((u'\n' + link).split(u'\n')) + result += u'\n' return result diff --git a/layman/overlays/rsync.py b/layman/overlays/rsync.py index e2483ad..18d563f 100644 --- a/layman/overlays/rsync.py +++ b/layman/overlays/rsync.py @@ -58,7 +58,7 @@ class RsyncOverlay(Overlay): self.supported() - return self.cmd(self.base + '"' + self.src + '/*" "' + + return self.cmd(self.base + '"' + self.src + '/" "' + path([base, self.name]) + '"') def supported(self): diff --git a/layman/overlays/tar.py b/layman/overlays/tar.py index 24a9b91..ce89450 100644 --- a/layman/overlays/tar.py +++ b/layman/overlays/tar.py @@ -63,7 +63,7 @@ class TarOverlay(Overlay): type = 'Tar' - binary = '/bin/tar' + binary = u'/bin/tar' def __init__(self, xml, ignore = 0, quiet = False): @@ -138,8 +138,8 @@ class TarOverlay(Overlay): os.makedirs(target) - result = self.cmd(self.binary + ' -v -x ' + opt + ' -f "' + pkg - + '" -C "' + target + '"') + result = self.cmd(self.binary + u' -v -x ' + opt + u' -f "' + pkg + + u'" -C "' + target + u'"') if self.subpath: source = target + '/' + self.subpath diff --git a/layman/tests/testfiles/overlays_bug_184449.xml b/layman/tests/testfiles/overlays_bug_184449.xml new file mode 100644 index 0000000..c8dff2d --- /dev/null +++ b/layman/tests/testfiles/overlays_bug_184449.xml @@ -0,0 +1,19 @@ + + + + + + + Test ä + + + + + + diff --git a/layman/version.py b/layman/version.py index 943522b..c740a18 100644 --- a/layman/version.py +++ b/layman/version.py @@ -18,7 +18,7 @@ __version__ = "$Id: version.py 309 2007-04-09 16:23:38Z wrobel $" -VERSION = '1.0.99' +VERSION = '1.1' if __name__ == '__main__': print VERSION -- cgit v1.2.3-1-g7c22 -- cgit v1.2.3-1-g7c22