SVN-Notify-Mirror-0.038/0000755000175000001440000000000011013430355014401 5ustar jpeacockusersSVN-Notify-Mirror-0.038/LICENSE0000444000175000001440000005010111013430350015374 0ustar jpeacockusersTerms of Perl itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --------------------------------------------------------------------------- The General Public License (GPL) Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 --------------------------------------------------------------------------- The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. Aggregation of this Package with a commercial distribution is always permitted provided that the use of this Package is embedded; that is, when no overt attempt is made to make this Package's interfaces visible to the end user of the commercial distribution. Such use shall not be construed as a distribution of this Package. 9. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End SVN-Notify-Mirror-0.038/Changes0000444000175000001440000000573411013430350015676 0ustar jpeacockusers[Changes for 0.037 - 2008-03-02] Release to CPAN [Changes for 0.03603 - 2007-05-16] Enabled rsync test to be optional (like ssh tests). Added Copyright text. Added explicit $VERSION to implementation classes (to match main $VERSION). [Changes for 0.03602 - 2007-03-14] * --minimal would fail to create new directory if there wasn't also a file modified higher in the tree. [Changes for 0.03601 - 2007-03-02] * Quick release to fix brokeness in --tag-regex handling. [Changes for 0.036 - 2007-03-02] * Handle multiple targets (i.e. To) which to_regex_map and S::N::Config can produce. * Improve and document tunneling of http[s]:// paths. [Changes for 0.035 - 2007-01-29] * New option '--minimal' to update the smallest containing path at each revision. Will lead to mixed revision working copies, but is guaranteed to be faster for deep trees. * All tests run once with and once without '--minimal' except for config-style which has that as a default (since it is a cheap operation). [Changes for 0.03403 - 2006-11-21] * Use lessons learned from testing SVN::Notify::Config about external binaries. No actual code changes, just test tweaks. [Changes for 0.03402 - 2006-11-01] * More robustness in tests. * Don't rely on svnadmin being in the path for testing. [Changes for 0.034 - 2006-07-07] * Quick release to cope with backwards incompatible API changes in SVN::Notify 2.61. * Depends on SVN-Notify-Config that is fixed for above as well. [Changes for 0.033 - 2006-02-25] * Depend on the newly minted SVN::Notify-2.53. * Use SVN::Notify->find_exe() to locate the 'svn' binary. [Changes for 0.032 - 2006-02-24] * Further improvements in making sure that the svn_binary is found. * Add explicit dependency on SVN::Notify. Duh. * Improve documentation of optional features. * Don't hardcode path to svn executable. [Changes for 0.031 - 2006-02-16] * Older Subversion clients choke if you give them the repos path when they weren't expecting it (go figure). [Changes for 0.03 - 2006-01-03] * Non-alpha release to CPAN of working Rsync and SSH subclasses. [Changes for 0.02_09 - 2005-12-29] * Re-add Rsync.pm and tests for same. [Changes for 0.02_07 - 2005-12-29] * Take Rsync.pm out until design questions are resolved. * Split out the SSH and add Rsync subclasses. * Complexify the testing regime to handle realworld repositories with multiple projects. * Rename all internal attributes to use underscore (as this is what YAML does anyways). * Working switch case for tags. [Changes for 0.02 - 2005-12-03] * Implement support for remote mirrors via SSH. * Structure to permit intelligent testing. * Replace underbar with hyphens in all parameters. * Remove dynamically created files from repository. * Add dynamic creation of Changes from repos log. * Add tests to compare files at each rev. [Changes for 0.01 - 2005-09-11] For some reason, I never added this to the repository before releasing to CPAN. I'm not even sure where I did this development now... ;-) SVN-Notify-Mirror-0.038/MANIFEST0000444000175000001440000000050211013430350015520 0ustar jpeacockusersBuild.PL Changes lib/SVN/Notify/Mirror.pm lib/SVN/Notify/Mirror/Rsync.pm lib/SVN/Notify/Mirror/SSH.pm LICENSE Makefile.PL MANIFEST MANIFEST.SKIP META.yml README SIGNATURE t/001_basic.t t/002_config.t.PL t/003_sshtest.t.PL t/004_rsynctest.t.PL t/coretests.pm t/test-repos.dump Todo SIGNATURE Added here by Module::Build SVN-Notify-Mirror-0.038/META.yml0000444000175000001440000000124511013430350015645 0ustar jpeacockusers--- name: SVN-Notify-Mirror version: 0.038 author: - 'John Peacock ' abstract: Keep a mirrored working copy of a repository path license: perl resources: license: http://dev.perl.org/licenses/ requires: Module::Build: 0.2805 SVN::Notify: 2.7 YAML: 0.62 provides: SVN::Notify::Mirror: file: lib/SVN/Notify/Mirror.pm version: 0.038 SVN::Notify::Mirror::Rsync: file: lib/SVN/Notify/Mirror/Rsync.pm version: 0.038 SVN::Notify::Mirror::SSH: file: lib/SVN/Notify/Mirror/SSH.pm version: 0.038 generated_by: Module::Build version 0.2808 meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.2.html version: 1.2 SVN-Notify-Mirror-0.038/t/0000755000175000001440000000000011013430350014637 5ustar jpeacockusersSVN-Notify-Mirror-0.038/t/test-repos.dump0000444000175000001440000001567511013430350017647 0ustar jpeacockusersSVN-fs-dump-format-version: 2 UUID: 01f5a4d4-d608-0410-877d-81f61bdc79cb Revision-number: 0 Prop-content-length: 56 Content-length: 56 K 8 svn:date V 27 2005-12-26T17:38:10.447180Z PROPS-END Revision-number: 1 Prop-content-length: 136 Content-length: 136 K 7 svn:log V 33 create structure for two projects K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:11.261127Z PROPS-END Node-path: project1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project1/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project1/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project1/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2/branches Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2/tags Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2/trunk Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Revision-number: 2 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 27 start work on first project K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:12.168538Z PROPS-END Node-path: project1/trunk/dir1 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project1/trunk/dir1/file2 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 3d709e89c8ce201e3c928eb917989aef Content-length: 16 PROPS-END file2 Node-path: project1/trunk/file1 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 5149d403009a139c7e085405ef762e1a Content-length: 16 PROPS-END file1 Revision-number: 3 Prop-content-length: 129 Content-length: 129 K 7 svn:log V 26 tag trunk before branching K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:13.138755Z PROPS-END Node-path: project1/tags/TRUNK-1135534439 Node-kind: dir Node-action: add Node-copyfrom-rev: 2 Node-copyfrom-path: project1/trunk Revision-number: 4 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 28 branch trunk for development K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:13.212676Z PROPS-END Node-path: project1/branches/branch1 Node-kind: dir Node-action: add Node-copyfrom-rev: 3 Node-copyfrom-path: project1/trunk Revision-number: 5 Prop-content-length: 130 Content-length: 130 K 7 svn:log V 27 simulate branch development K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:14.173773Z PROPS-END Node-path: project1/branches/branch1/dir2 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project1/branches/branch1/dir2/file4 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 857c6673d7149465c8ced446769b523c Content-length: 16 PROPS-END file4 Node-path: project1/branches/branch1/file3 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 60b91f1875424d3b4322b0fdd0529d5d Content-length: 16 PROPS-END file3 Revision-number: 6 Prop-content-length: 121 Content-length: 121 K 7 svn:log V 18 merge from branch1 K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:15.206890Z PROPS-END Node-path: project1/trunk/dir2 Node-kind: dir Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: project1/branches/branch1/dir2 Node-path: project1/trunk/file3 Node-kind: file Node-action: add Node-copyfrom-rev: 5 Node-copyfrom-path: project1/branches/branch1/file3 Revision-number: 7 Prop-content-length: 138 Content-length: 138 K 7 svn:log V 35 tag trunk after merging from branch K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:16.135251Z PROPS-END Node-path: project1/tags/TRUNK-1135538253 Node-kind: dir Node-action: add Node-copyfrom-rev: 6 Node-copyfrom-path: project1/trunk Revision-number: 8 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 28 begin work on second project K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:16.258746Z PROPS-END Node-path: project2/trunk/dir3 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2/trunk/dir3/file6 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: 7dec9b529c0261ecbf1f32f21f0438d9 Content-length: 16 PROPS-END file6 Node-path: project2/trunk/dir4 Node-kind: dir Node-action: add Prop-content-length: 10 Content-length: 10 PROPS-END Node-path: project2/trunk/dir4/file7 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: badc2e9ea90821b51e7624168b50b9b5 Content-length: 16 PROPS-END file7 Node-path: project2/trunk/dir4/file8 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: cde9257d7ea4d74c45b3755b4e5e9b46 Content-length: 16 PROPS-END file8 Node-path: project2/trunk/file5 Node-kind: file Node-action: add Prop-content-length: 10 Text-content-length: 6 Text-content-md5: f1ad8dddbf5c5f61ce4cb6fd502f4625 Content-length: 16 PROPS-END file5 Revision-number: 9 Prop-content-length: 129 Content-length: 129 K 7 svn:log V 26 tag trunk before branching K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:17.138369Z PROPS-END Node-path: project2/tags/TRUNK-1135538991 Node-kind: dir Node-action: add Node-copyfrom-rev: 8 Node-copyfrom-path: project2/trunk Revision-number: 10 Prop-content-length: 131 Content-length: 131 K 7 svn:log V 28 branch trunk for development K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:17.207859Z PROPS-END Node-path: project2/branches/branch2 Node-kind: dir Node-action: add Node-copyfrom-rev: 9 Node-copyfrom-path: project2/trunk Revision-number: 11 Prop-content-length: 127 Content-length: 127 K 7 svn:log V 24 restructure branch files K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:20.145368Z PROPS-END Node-path: project2/branches/branch2/dir5 Node-kind: dir Node-action: add Node-copyfrom-rev: 10 Node-copyfrom-path: project2/branches/branch2/dir3 Node-path: project2/branches/branch2/file5.new Node-kind: file Node-action: add Node-copyfrom-rev: 10 Node-copyfrom-path: project2/branches/branch2/file5 Node-path: project2/branches/branch2/file5 Node-action: delete Node-path: project2/branches/branch2/dir3 Node-action: delete Revision-number: 12 Prop-content-length: 138 Content-length: 138 K 7 svn:log V 35 tag trunk after merging from branch K 10 svn:author V 8 jpeacock K 8 svn:date V 27 2005-12-26T17:38:22.198966Z PROPS-END Node-path: project2/tags/TRUNK-1135539568 Node-kind: dir Node-action: add Node-copyfrom-rev: 11 Node-copyfrom-path: project2/trunk SVN-Notify-Mirror-0.038/t/001_basic.t0000444000175000001440000000055111013430350016464 0ustar jpeacockusers#!/usr/bin/perl require SVN::Notify; use Test::More; require "t/coretests.pm"; SKIP: { my $SVNNOTIFY = $ENV{'SVNNOTIFY'} || SVN::Notify->find_exe('svnnotify'); skip "Cannot locate svnnotify binary!", 54 unless defined($SVNNOTIFY); reset_all_tests(); run_tests($SVNNOTIFY); reset_all_tests(); run_tests("$SVNNOTIFY --minimal"); } SVN-Notify-Mirror-0.038/t/003_sshtest.t.PL0000444000175000001440000000117011013430350017412 0ustar jpeacockusersuse Module::Build; my $filename = shift; my $mb = Module::Build->current(); if ( $mb->feature('ssh_support') and $mb->notes('sshtests') ) { open my $FILE, '>', $filename; print {$FILE} <<"EOF"; require SVN::Notify; use Test::More; require "t/coretests.pm"; SKIP: { my \$SVNNOTIFY = \$ENV{'SVNNOTIFY'} || SVN::Notify->find_exe('svnnotify'); skip "Cannot locate svnnotify binary!", 54 unless defined(\$SVNNOTIFY); reset_all_tests(); run_tests(\$SVNNOTIFY, 'ssh-host' => 'localhost' ); reset_all_tests(); run_tests("\$SVNNOTIFY --minimal", 'ssh-host' => 'localhost' ); } EOF close $FILE; } SVN-Notify-Mirror-0.038/t/002_config.t.PL0000444000175000001440000000305211013430350017162 0ustar jpeacockusersuse Module::Build; my $mb = Module::Build->current(); exit(0) unless $mb->feature('config_support'); use Cwd; my ($testconfig, $maintest, $subtest) = (shift, shift, shift); my $PWD = getcwd; # setup the configuration file open my $FILE, '>', $testconfig; print {$FILE} <<"EOF"; #!/usr/bin/perl -MSVN::Notify::Config=\$0 --- #YAML:1.0 '': PATH: "/usr/local/bin:/usr/bin" '/project1/branches/branch1': handler: Mirror to: "$PWD/t/wc-branch/" EOF if ( $mb->feature('ssh_support') and $mb->notes('sshtests') ) { print {$FILE} <<"EOF"; '/project1/trunk': handler: Mirror::SSH to: "$PWD/t/wc-trunk/" minimal: 1 ssh-host: localhost '/project1/tags': handler: Mirror::SSH to: "$PWD/t/wc-tag/" tag-regex: 'TRUNK' ssh-host: localhost EOF } else { print {$FILE} <<"EOF"; '/project1/trunk': handler: Mirror minimal: 1 to: "$PWD/t/wc-trunk/" '/project1/tags': handler: Mirror to: "$PWD/t/wc-tag/" tag-regex: 'TRUNK' EOF } close $FILE; open my $FILE, '>', $subtest; print {$FILE} <<"EOF"; #!/usr/bin/perl -w # We have to go through this roundabout method because # SVN::Notify::Config->import() won't run with -I on the # command line and because it calls exit at the end BEGIN { unshift \@INC, "blib/lib", "blib/arch"; } my (\$repos_path, \$rev) = (shift, shift); require SVN::Notify::Config; push \@ARGV, \$repos_path, \$rev; SVN::Notify::Config->import( "$testconfig" ); EOF close $FILE; open my $FILE, '>', $maintest; print {$FILE} <<"EOF"; require "t/coretests.pm"; reset_all_tests(); run_tests("perl $subtest"); EOF close $FILE; SVN-Notify-Mirror-0.038/t/coretests.pm0000444000175000001440000004647711013430350017230 0ustar jpeacockusers#!/usr/bin/perl use Test::More; #use Data::Dumper; use SVN::Notify; use Cwd; use Config; my $SECURE_PERL_PATH = $Config{perlpath}; if ($^O ne 'VMS') { $SECURE_PERL_PATH.= $Config{_exe} unless $SECURE_PERL_PATH =~ m/$Config{_exe}$/i; } my $PWD = getcwd; my $USER = $ENV{USER}; my $SVNLOOK = $ENV{SVNLOOK} || SVN::Notify->find_exe('svnlook'); my $SVNADMIN = $ENV{SVNADMIN} || SVN::Notify->find_exe('svnadmin'); if ( !defined($SVNLOOK) ) { plan skip_all => "Cannot find svnlook!\n". "Please start the tests like this:\n". " SVNLOOK=/path/to/svnlook make test"; } elsif ( !defined($SVNADMIN) ) { plan skip_all => "Cannot find svnadmin!\n". "Please start the tests like this:\n". " SVNADMIN=/path/to/svnadmin make test"; } else { plan no_plan; } my $repos_path = "$PWD/t/test-repos"; my $wc_map = { 'wc-trunk' => { path => 'project1/trunk', base_rev => 1, command => 'update', }, 'wc-tag' => { path => 'project1/tags/TRUNK-1135534439', base_rev => 3, command => 'switch', switch_rev => 7, switch_path => 'project1/tags/TRUNK-1135538253', tag_regex => 'TRUNK-', }, 'wc-branch' => { path => 'project1/branches/branch1', base_rev => 4, command => 'update', }, }; my $wc_rsync_map; foreach my $key ( keys %$wc_map ) { $wc_rsync_map->{$key.'-r'} = $wc_map->{$key}; } my ($repos_history, $changes); my $eval; while () { $eval .= $_; } eval $eval; my $maxrev = 7; # change this later to be the actual number of revs sub reset_all_tests { create_test_repos(); create_test_wcs(); reset_test_wcs(); } # Create a repository fill it with sample values the first time through sub create_test_repos { unless ( -d $repos_path ) { system(<<"") == 0 or die "system failed: $?"; $SVNADMIN create $repos_path system(<<"") == 0 or die "system failed: $?"; $SVNADMIN load --quiet $repos_path < ${repos_path}.dump } } # Create test WC's before proceeding with tests the first time sub create_test_wcs { unless ( -d "$PWD/t/wc-trunk" ) { foreach my $wc ( keys %{$wc_map} ) { my $path = $wc_map->{$wc}->{'path'}; my $rev = $wc_map->{$wc}->{'base_rev'}; my $cmd = "svn checkout -q -r$rev ". "file://$repos_path/$path $PWD/t/$wc"; system($cmd) == 0 or die "system failed: $?"; } } } # Reset the working copies sub reset_test_wcs { foreach my $wc ( keys %{$wc_map} ) { my $path = $wc_map->{$wc}->{'path'}; my $rev = $wc_map->{$wc}->{'base_rev'}; my $command = $wc_map->{$wc}->{'command'}; my $cmd = "svn $command -q -r$rev "; $cmd .= "file://$repos_path/$path " if ( $command =~ /switch/ ); # accomodate older svn's $cmd .= "$PWD/t/$wc"; system($cmd) == 0 or die "system failed: $?"; } } sub run_tests { my $command = shift; my $TESTER; my $rsync_test = 0; for (my $rev = 1; $rev <= $maxrev; $rev++) { foreach my $wc ( keys %{$wc_map} ) { next unless $rev >= $wc_map->{$wc}->{'base_rev'}; my %args = @_; # Common to all tests $args{'repos-path'} = $repos_path; $args{'handler'} = defined $args{'ssh-host'} ? 'Mirror::SSH' : defined $args{'rsync-host'} ? 'Mirror::Rsync' : 'Mirror'; $args{'to'} = "$PWD/t/$wc"; $args{'revision'} = $rev; my $path = $wc_map->{$wc}->{'path'}; my $change = $changes->[$rev]->{$path} if exists $changes->[$rev]->{$path}; next unless $change; # special case the switched directories if ( $wc_map->{$wc}->{'command'} eq 'switch' && $rev >= $wc_map->{$wc}->{'switch_rev'} ) { $path = $wc_map->{$wc}->{'switch_path'}; $args{'tag-regex'} = $wc_map->{$wc}->{'tag_regex'}; } # need to specify destination for rsync tests if ( defined $args{'rsync-host'} ) { $rsync_test = 1; $args{'rsync-dest'} = "$PWD/t/$wc\-r"; } _test( $change, $path, $command, %args ); } _compare_directories($rev, $wc_map); if ($rsync_test) { _compare_directories($rev, $wc_rsync_map); $rsync_test = 0; } } } sub _test { my ($expected, $prefix, $command, %args) = @_; my $test = {}; open $TESTER, '-|', _build_command($command, %args); while (<$TESTER>) { chomp; if ( /^At revision (\d+)\./ ) { ok ( $1 == $args{revision} , "No changes in $prefix at revision: " . $args{revision} ); last; # no need to read any more input } elsif ( /^Updated to revision (\d+)\./ ) { ok ( $1 == $args{revision} , "Updated $prefix to correct revision: " . $args{revision} ); } else { my ($status, $target) = split; $test->{$prefix.'/'.$target} = $status; } } close $TESTER; is_deeply( $test, $expected, "Correct files updated in $prefix at rev: " . $args{revision} ) if scalar(keys %$test) > 0; } sub _build_command { my ($command, %args) = @_; my @commandline = split " ", $command; if ( $command =~ /svnnotify/ ) { # hate to hardcode this, but what else can we do foreach my $key ( keys(%args) ) { push @commandline, "\-\-$key", $args{$key}; } } else { push @commandline, $args{'repos-path'}, $args{'revision'}; } return @commandline; } sub _compare_directories { my ($rev, $wc_hash) = @_; my $history = $repos_history->[$rev]; my $this_rev = {}; foreach my $wc ( keys %$wc_hash ) { next unless $rev >= $wc_hash->{$wc}->{'base_rev'}; my $subhistory = _expand_path($history, $wc_hash->{$wc}->{'path'}); $this_rev = _scan_dir("t/$wc"); is_deeply( $subhistory, $this_rev, "Directories are consistent at rev: $rev" ); } } sub _expand_path { my ($tree, $path) = @_; my @paths = split('/',$path); my $eval = "\$tree->{'".join("'}->{'", @paths)."'}"; return eval $eval; } sub _scan_dir { my ($dir) = @_; my $fsize; my $this_rev = {}; opendir my($DIR), $dir; my @directory = grep !/^\..*/, readdir $DIR; closedir $DIR; foreach my $file ( @directory ) { if ( -d "$dir/$file" ) { $this_rev->{$file} = _scan_dir( "$dir/$file" ); } elsif ( ( -f "$dir/$file" ) && ( my $size = -s "$dir/$file" ) ) { $this_rev->{$file} = $size; } } return defined $this_rev ? $this_rev : {}; } 1; # magic return __DATA__ $repos_history = [ {}, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } }, 'branches' => {}, 'tags' => {} } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } }, 'branches' => {}, 'tags' => { 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => {}, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'branches' => {}, 'tags' => {} }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'branches' => {}, 'tags' => { 'TRUNK-1135538991' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } } } }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'branches' => { 'branch2' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } } }, 'tags' => { 'TRUNK-1135538991' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } } } }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'branches' => { 'branch2' => { 'file5.new' => '6', 'dir5' => { 'file6' => '6' }, 'dir4' => { 'file7' => '6', 'file8' => '6' } } }, 'tags' => { 'TRUNK-1135538991' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } } } }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } }, { 'project2' => { 'trunk' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'branches' => { 'branch2' => { 'file5.new' => '6', 'dir5' => { 'file6' => '6' }, 'dir4' => { 'file7' => '6', 'file8' => '6' } } }, 'tags' => { 'TRUNK-1135539568' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } }, 'TRUNK-1135538991' => { 'dir3' => { 'file6' => '6' }, 'file5' => '6', 'dir4' => { 'file7' => '6', 'file8' => '6' } } } }, 'project1' => { 'trunk' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'branches' => { 'branch1' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } } }, 'tags' => { 'TRUNK-1135538253' => { 'file1' => '6', 'file3' => '6', 'dir2' => { 'file4' => '6' }, 'dir1' => { 'file2' => '6' } }, 'TRUNK-1135534439' => { 'file1' => '6', 'dir1' => { 'file2' => '6' } } } } } ]; $changes = [ {}, { '' => { 'project2' => 'A', 'project1' => 'A', 'project2/tags' => 'A', 'project1/trunk' => 'A', 'project2/branches' => 'A', 'project2/trunk' => 'A', 'project1/branches' => 'A', 'project1/tags' => 'A' }, }, { 'project1/trunk' => { 'project1/trunk/file1' => 'A', 'project1/trunk/dir1/file2' => 'A', 'project1/trunk/dir1' => 'A' }, }, { 'project1/trunk' => {}, 'project1/tags/TRUNK-1135534439' => { 'project1/tags/TRUNK-1135534439/file1' => 'A', 'project1/tags/TRUNK-1135534439/dir1/file2' => 'A', 'project1/tags/TRUNK-1135534439/dir1' => 'A' }, }, { 'project1/trunk' => {}, 'project1/tags/TRUNK-1135534439' => {}, 'project1/branches/branch1', { 'project1/branches/branch1/file1' => 'A', 'project1/branches/branch1/dir1/file2' => 'A', 'project1/branches/branch1/dir1' => 'A' }, }, { 'project1/branches/branch1' => { 'project1/branches/branch1/dir2/file4' => 'A', 'project1/branches/branch1/file3' => 'A', 'project1/branches/branch1/dir2' => 'A' }, }, { 'project1/trunk' => { 'project1/trunk/file3' => 'A', 'project1/trunk/dir2' => 'A', 'project1/trunk/dir2/file4' => 'A' }, }, { 'project1/tags/TRUNK-1135538253' => { 'project1/tags/TRUNK-1135538253/file3' => 'A', 'project1/tags/TRUNK-1135538253/dir2' => 'A', 'project1/tags/TRUNK-1135538253/dir2/file4' => 'A' }, }, { 'project2/trunk' => { 'project2/trunk/dir4/file7' => 'A', 'project2/trunk/dir4/file8' => 'A', 'project2/trunk/dir3/file6' => 'A', 'project2/trunk/dir3' => 'A', 'project2/trunk/file5' => 'A', 'project2/trunk/dir4' => 'A' }, }, { 'project2/tags/TRUNK-1135538991' => { 'project2/tags/TRUNK-1135538991/dir4/file7' => 'A', 'project2/tags/TRUNK-1135538991/dir4/file8' => 'A', 'project2/tags/TRUNK-1135538991/dir3/file6' => 'A', 'project2/tags/TRUNK-1135538991/dir3' => 'A', 'project2/tags/TRUNK-1135538991/file5' => 'A', 'project2/tags/TRUNK-1135538991/dir4' => 'A' }, }, { 'project2/branches/branch2' => { 'project2/branches/branch2' => 'A' }, }, { 'project2/branches/branch2' => { 'project2/branches/branch2/dir5' => 'A', 'project2/branches/branch2/file5' => 'D', 'project2/branches/branch2/dir3' => 'D', 'project2/branches/branch2/file5.new' => 'A' }, }, { 'project2/tags/TRUNK-1135539568' => { 'project2/tags/TRUNK-1135539568' => 'A' } }, ]; SVN-Notify-Mirror-0.038/t/004_rsynctest.t.PL0000444000175000001440000000135111013430350017755 0ustar jpeacockusersuse Module::Build; my $filename = shift; my $mb = Module::Build->current(); if ( $mb->feature('rsync_support') and $mb->notes('rsynctests') ) { my $ssh = ''; if ( $mb->feature('rsyncsshtests') ) { $ssh = "'rsync-ssh' => 1"; } open my $FILE, '>', $filename; print {$FILE} <<"EOF"; require SVN::Notify; use Test::More; require "t/coretests.pm"; SKIP: { my \$SVNNOTIFY = \$ENV{'SVNNOTIFY'} || SVN::Notify->find_exe('svnnotify'); skip "Cannot locate svnnotify binary!", 78 unless defined(\$SVNNOTIFY); reset_all_tests(); run_tests(\$SVNNOTIFY, 'rsync-host' => 'localhost', $ssh); reset_all_tests(); run_tests("\$SVNNOTIFY --minimal", 'rsync-host' => 'localhost', $ssh); } EOF close $FILE; } SVN-Notify-Mirror-0.038/Makefile.PL0000444000175000001440000000172311013430350016347 0ustar jpeacockusersunless (eval "use Module::Build::Compat 0.02; 1" ) { print "This module requires Module::Build to install itself.\n"; my $isa_tty = -t STDIN && (-t STDOUT || !(-f STDOUT || -c STDOUT)) ; if ($ENV{PERL_MM_USE_DEFAULT} || (!$isa_tty && eof STDIN)) { exit 1; } require ExtUtils::MakeMaker; my $yn = ExtUtils::MakeMaker::prompt (' Install Module::Build from CPAN?', 'y'); if ($yn =~ /^y/i) { require Cwd; require File::Spec; require CPAN; # Save this 'cause CPAN will chdir all over the place. my $cwd = Cwd::cwd(); my $makefile = File::Spec->rel2abs($0); CPAN::Shell->install('Module::Build::Compat'); chdir $cwd or die "Cannot chdir() back to $cwd: $!"; exec $^X, $makefile, @ARGV; # Redo now that we have Module::Build } else { warn " *** Cannot install without Module::Build. Exiting ...\n"; exit 1; } } Module::Build::Compat->run_build_pl(args => \@ARGV); Module::Build::Compat->write_makefile(); SVN-Notify-Mirror-0.038/Todo0000444000175000001440000000007711013430350015226 0ustar jpeacockusersTODO list for Perl module SVN::Notify::Mirror - Nothing yet SVN-Notify-Mirror-0.038/SIGNATURE0000644000175000001440000000344311013430355015671 0ustar jpeacockusersThis file contains message digests of all files listed in MANIFEST, signed via the Module::Signature module, version 0.55. To verify the content in this distribution, first make sure you have Module::Signature installed, then type: % cpansign -v It will check each file's integrity, as well as the signature's validity. If "==> Signature verified OK! <==" is not displayed, the distribution may already have been compromised, and you should not run its Makefile.PL or Build.PL. -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 SHA1 fe157888bb9d9b4a9161122834bb3ed0c1564cb7 Build.PL SHA1 7b7d7abdbe3bd8a2ecec574935a14d403959b82a Changes SHA1 f235ba4160673bcb7c9d58c2f09dbc7fc0efadea LICENSE SHA1 39eeb03ee23fb784d0d088dbb7d3d6a9b4236db7 MANIFEST SHA1 7281666fe4dbb3f1cccd4d64a3a56930c8b3ab29 MANIFEST.SKIP SHA1 ec3c922af9dec3cc44b16b764abf98bfd63af5d7 META.yml SHA1 96dc70ff02cd5002108c56f4579806e68a1a0452 Makefile.PL SHA1 1e7e70275b21edaa06b6c13ec80532af3e56899d README SHA1 7e5e3601384ff724512e4eec5540f4e1577a850d Todo SHA1 2c417aacb1d92c6985c6ab56515c0faf97d66c4b lib/SVN/Notify/Mirror.pm SHA1 551d2d09d8320b28de750ccc51dd1d22e27276e8 lib/SVN/Notify/Mirror/Rsync.pm SHA1 8f192a33183346f5ad6280fb0bb84759c85653e3 lib/SVN/Notify/Mirror/SSH.pm SHA1 2828d9959617fc9c1369a729c30fe0b76e7d05ff t/001_basic.t SHA1 0152c628dce1442d18e319700101d7157c8f921a t/002_config.t.PL SHA1 eeca73e531262ec5152610fff1d59f2e1a503601 t/003_sshtest.t.PL SHA1 21452b84efe89d7f35cb2ae742807e7e0718fed0 t/004_rsynctest.t.PL SHA1 9417ac53a16259e5cc589300f2c206cd07fe39d4 t/coretests.pm SHA1 af719d83aa699b230d2f1853f2b4e4e94644ad3e t/test-repos.dump -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.4-svn0 (GNU/Linux) iD8DBQFILjDtimg5mLGPahYRAoBcAJ4sq15iUY4iomdux+aVyGRXT4s/+gCeNO5m zxZr8SXK1A/ZKSH7dopmLZ8= =qST6 -----END PGP SIGNATURE----- SVN-Notify-Mirror-0.038/Build.PL0000444000175000001440000000357211013430350015675 0ustar jpeacockusersuse Module::Build; # See perldoc Module::Build for details of how this works my $class = Module::Build->subclass ( class => 'JPEACOCK::Builder', code => q{ sub ACTION_dist{ my $self = shift; $self->do_system('my-changelog.pl > Changes'); $self->SUPER::ACTION_dist(); } } ); my $mb = $class->new ( module_name => 'SVN::Notify::Mirror', license => 'perl', dist_author => 'John Peacock ', sign => 1, requires => { 'Module::Build' => 0.2805, 'SVN::Notify' => 2.70, 'YAML' => 0.62, }, auto_features => { ssh_support => { description => "Use remote mirrors via ssh", requires => {Net::SSH => 0.08 } }, config_support => { description => "Use compact YAML script", requires => {SVN::Notify::Config => 0.0906} }, rsync_support => { description => "Transfer files with rsync", requires => {File::Rsync => 0.42} }, }, PL_files => { 't/002_config.t.PL' => ['t/testconfig','t/002_config.t','t/002_config'], 't/003_sshtest.t.PL' => 't/003_sshtest.t', 't/004_rsynctest.t.PL' => 't/004_rsynctest.t', }, add_to_cleanup => ['t/test-repos', 't/wc-*', 't/00[2-4]*.t'], ); if ( $mb->feature('ssh_support') ) { $mb->notes( sshtests => 1 ) if $mb->y_n( "Do you want to run the SSH tests?\n". "(see the README for details)",'N'); } if ( $mb->feature('rsync_support') ) { $mb->notes( rsynctests => 1 ) if $mb->y_n( "Do you want to run the rsync tests?\n". "(see the README for details)",'N'); if ( $mb->notes('sshtests') && $mb->notes('rsynctests') ) { $mb->notes( rsyncsshtests => 1 ) if $mb->y_n( "Do you want to run the rsync tests via SSH?\n". "(see the README for details)",'N'); } } $mb->create_build_script; SVN-Notify-Mirror-0.038/lib/0000755000175000001440000000000011013430350015142 5ustar jpeacockusersSVN-Notify-Mirror-0.038/lib/SVN/0000755000175000001440000000000011013430350015610 5ustar jpeacockusersSVN-Notify-Mirror-0.038/lib/SVN/Notify/0000755000175000001440000000000011013430350017060 5ustar jpeacockusersSVN-Notify-Mirror-0.038/lib/SVN/Notify/Mirror/0000755000175000001440000000000011013430350020332 5ustar jpeacockusersSVN-Notify-Mirror-0.038/lib/SVN/Notify/Mirror/Rsync.pm0000444000175000001440000001553611013430350021776 0ustar jpeacockusers#!/usr/bin/perl -w package SVN::Notify::Mirror::Rsync; use strict; BEGIN { use vars qw ($VERSION); use base qw(SVN::Notify::Mirror); $VERSION = 0.038; } __PACKAGE__->register_attributes( 'rsync_host' => 'rsync-host=s', 'rsync_args' => 'rsync-args=s%', 'rsync_dest' => 'rsync-dest=s', 'rsync_delete' => 'rsync-delete', 'rsync_ssh' => 'rsync-ssh', 'ssh_user' => 'ssh-user:s', 'ssh_binary' => 'ssh-binary:s', 'ssh_identity' => 'ssh-identity:s', 'ssh_options' => 'ssh-options:s', ); sub _cd_run { my ($self, $path, $binary, $command, @args) = @_; my @message = $self->SUPER::_cd_run($path, $binary, $command, @args); eval "use File::Rsync"; die "Failed to load File::Rsync: $@" if $@; my $host = $self->{'rsync_host'} || 'localhost'; my $dest = $self->{'rsync_dest'} || $path; # Set some common arguments to pass to new() my $args; if ( defined $self->{'rsync_args'} ) { $args = $self->{'rsync_args'}; } else { $args = { archive => 1, compress => 1, }; } $args->{delete} = 1 unless defined $self->{'rsync_delete'} and $self->{'rsync_delete'} == 0; push @{$args->{'exclude'}}, '.svn'; # $args->{debug} = 1; # define the ssh options if necessary if ( defined $self->{'rsync_ssh'} ) { # Check for various ssh options my $ssh_binary = (defined $self->{'ssh_binary'} ? $self->{'ssh_binary'} : '/usr/bin/ssh'); my $ssh_user = (defined $self->{'ssh_user'} ? $self->{'ssh_user'} : ""); my $ssh_identity = (defined $self->{'ssh_identity'} ? $self->{'ssh_identity'} : ""); my $ssh_options = (defined $self->{'ssh_options'} ? $self->{'ssh_options'} : ""); $args->{'rsh'} = $ssh_binary . ($ssh_user ? " -l $ssh_user" : "") . ($ssh_identity ? " -i $ssh_identity" : "") . ($ssh_options ? " $ssh_options" : ""); } my $rsync = File::Rsync->new($args); $rsync->exec( { src => $path."/", dest => "$host:$dest", }) or push @message, "rsync failed:\n" . join("\n",$rsync->err); return (@message); } 1; __END__ ########################################### main pod documentation begin ## =head1 NAME SVN::Notify::Mirror::Rsync - Mirror a repository path via Rsync =head1 SYNOPSIS Use F in F: svnnotify --repos-path "$1" --revision "$2" \ --handler Mirror::Rsync --to "/path/to/local/htdocs" \ [--svn-binary /full/path/to/svn] \ --rsync-host remote_server \ [--rsync-delete=[yes|no]] \ [--rsync-dest "/path/on/remote/server"] \ [--rsync-args arg1 [--rsync-args arg2...]] [[--rsync-ssh] [--ssh-user remote_user] \ [--ssh-identity /home/user/.ssh/id_rsa]] or better yet, use L for a more sophisticated setup: #!/usr/bin/perl -MSVN::Notify::Config=$0 --- #YAML:1.0 '': PATH: "/usr/bin:/usr/local/bin" 'path/in/repository': handler: Mirror to: "/path/to/www/htdocs" 'some/other/path/in/repository': handler: Mirror to: "/path/to/local/www/htdocs" rsync-host: "remote_host" rsync-dest: "/path/on/remote/www/htdocs" ssh-user: "remote_user" ssh-identity: "/home/user/.ssh/id_rsa" =head1 DESCRIPTION Keep a directory in sync with a portion of a Subversion repository. Typically used to keep a development web server in sync with the changes made to the repository. This directory can either be on the same box as the repository itself, or it can be remote (via SSH connection). =head1 USAGE Depending on whether the target is a L or a L, there are different options available. All options are available either as a commandline option to svnnotify or as a hash key in L (see their respective documentation for more details). =head2 Working Copy on Local host Because 'svn export' is not able to be consistently updated, the local rsync'd directory must be a full working copy. The remote server will only contain the ordinary files (no Subversion admin files). The files in the working copy must be writeable (preferrably owned) by the user identity executing the hook script (this is the user identity that is running Apache or svnserve respectively). =head1 Local Mirror Please see L< SVN::Notify::Mirror > for details. =head2 Remote Mirror Used for directories not located on the same machine as the repository itself. Typically, this might be a production web server located in a DMZ, so special consideration must be paid to security concerns. In particular, the remote mirror server may not be able to directly access the repository box. =over 4 =item * rsync-host This value is required and must be the hostname or IP address of the remote host (where the mirror directories reside). =item * rsync-delete The default mode of operation is to delete remote files which are not present in the local working copy. NOTE: this will B any unversioned files in the remote directory tree. Unless you have all of your files under version control, you should pass the C<--no-rsync-delete> or C<--rsync-delete no> option. =item * rsync-dest This optional value specifies the path to update on the remote host. If you do not specify this value, the same path as passed in as the C<--to> parameter will be used (this may not be what you meant to do). =item * rsync-args This optional parameter can be used to pass additional commandline options to the rsync command. You can use this multiple times in order to pass multiple options. The default args are C<--archive --compress>. See the C options for using SSH instead of RSH (rather than pass those commands via C<--rsync-args> =item * rsync-ssh This optional parameter signals that you wish to use SSH instead of whatever the default remote shell program is configured in your copy of rsync. You may need to set one or more of the C parameters as well. =item * ssh-user If the remote user is different than the local user executing the postcommit script, you can specify it with this parameter. You would often use this in conjunction with the next parameter. =item * ssh-identity This value may be optional and should be the full path to the local identity file being used to authenticate with the remote host. If you are setting the ssh-user to be something other than the local user name, you will typically also have to set the ssh-identity. =back =head1 AUTHOR John Peacock =head1 COPYRIGHT Copyright (c) 2005-2008 John Peacock This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO L, L, L =cut ############################################# main pod documentation end ## SVN-Notify-Mirror-0.038/lib/SVN/Notify/Mirror/SSH.pm0000444000175000001440000002172311013430350021330 0ustar jpeacockusers#!/usr/bin/perl -w package SVN::Notify::Mirror::SSH; use strict; BEGIN { use vars qw ($VERSION); use base qw(SVN::Notify::Mirror); $VERSION = 0.038; } __PACKAGE__->register_attributes( 'ssh_host' => 'ssh-host=s', 'ssh_user' => 'ssh-user:s', 'ssh_tunnel' => 'ssh-tunnel:s', 'ssh_identity' => 'ssh-identity:s', 'ssh_options' => 'ssh-options:s', ); sub _cd_run { my ($self, $path, $binary, $command, @args) = @_; eval "use Net::SSH qw(sshopen2)"; die "Failed to load Net::SSH: $@" if $@; my $host = $self->{'ssh_host'}; my $user = defined $self->{'ssh_user'} ? $self->{'ssh_user'}.'@'.$host : $host; my @message; unshift @args, $binary, $command; $path =~ s/'/'"'"'/g; # quote single quotes my $cmd = "cd '$path' && " . join(" ",@args); # wrap path in single quotes if ( defined $self->{'ssh_tunnel'} ) { if ( $self->{'ssh_tunnel'} =~ m/\d+:.+:\d+/ ) { # user-supplied configuration push @Net::SSH::ssh_options, '-R'.$self->{'ssh_tunnel'}, '-q'; } else { # default svnserve configuration push @Net::SSH::ssh_options, "-R3690:".$self->{'ssh_tunnel'}.":3690"; } } if ( defined $self->{'ssh_identity'} ) { push @Net::SSH::ssh_options, "-i".$self->{'ssh_identity'}; } if ( defined $self->{'ssh_options'} ) { push @Net::SSH::ssh_options, split(" ",$self->{'ssh_options'}); } sshopen2($user, *READER, *WRITER, $cmd) || die "ssh: $!"; while () { chomp; push @message, $_; } close(READER); close(WRITER); return (@message); } 1; __END__ ########################################### main pod documentation begin ## =head1 NAME SVN::Notify::Mirror::SSH - Mirror a repository path via SSH =head1 SYNOPSIS Use F in F: svnnotify --repos-path "$1" --revision "$2" \ --handler Mirror::SSH --to "/path/to/www/htdocs" \ [--svn-binary /full/path/to/svn] \ [[--ssh-host remote_host] [--ssh-user remote_user] \ [--ssh-tunnel 10.0.0.2] \ [--ssh-identity /home/user/.ssh/id_rsa]] or better yet, use L for a more sophisticated setup: #!/usr/bin/perl -MSVN::Notify::Config=$0 --- #YAML:1.0 '': PATH: "/usr/bin:/usr/local/bin" 'path/in/repository': handler: Mirror to: "/path/to/www/htdocs" 'some/other/path/in/repository': handler: Mirror::SSH to: "/path/to/remote/www/htdocs" ssh-host: "remote_host" ssh-user: "remote_user" ssh-tunnel: "10.0.0.2" ssh-identity: "/home/user/.ssh/id_rsa" =head1 DESCRIPTION Keep a directory in sync with a portion of a Subversion repository. Typically used to keep a development web server in sync with the changes made to the repository. This directory can either be on the same box as the repository itself, or it can be remote (via SSH connection). =head1 USAGE Depending on whether the target is a L or a L, there are different options available. All options are available either as a commandline option to svnnotify or as a hash key in L (see their respective documentation for more details). =head2 Working Copy on Mirror Because 'svn export' is not able to be consistently updated, the sync'd directory must be a full working copy, and if you are running Apache, you should add lines like the following to your Apache configuration file: # Disallow browsing of Subversion working copy # administrative directories. Order deny,allow Deny from all The files in the working copy must be writeable (preferrably owned) by the user identity executing the hook script (this is the user identity that is running Apache or svnserve respectively). =head1 Local Mirror Please see L< SVN::Notify::Mirror > for details. =head2 Remote Mirror Used for directories not located on the same machine as the repository itself. Typically, this might be a production web server located in a DMZ, so special consideration must be paid to security concerns. In particular, the remote mirror server may not be able to directly access the repository box. NOTE: be sure and consult L before configuring your post-commit hook. =over 4 =item * ssh-host This value is required and must be the hostname or IP address of the remote host (where the mirror directories reside). =item * ssh-user This value is optional and specifies the remote username that owns the working copy mirror. =item * ssh-identity This value may be optional and should be the full path to the local identity file being used to authenticate with the remote host. If you are setting the ssh-user to be something other than the local user name, you will typically also have to set the ssh-identity. =item * ssh-tunnel If the remote server does not have direct access to the repository server, it is possible to use the tunneling capabilities of SSH to provide temporary access to the repository. This works even if repository is located internally, and the remote server is located outside of a firewall or on a DMZ. The value passed for ssh-tunnel should be the IP address to which the local repository service is bound (when using svnserve). This will tunnel port 3690 from the repository box to localhost:3690 on the remote box. This must also be the way that the original working copy was checked out (see below). To tunnel some other port, for example when using Apache/mod_dav, ssh-tunnel should be the entire mapping expression, as described in the OpenSSH documentation under the C<-R> option (remote port forwarding). For most sites, passing C<8080:10.0.0.2:80> will work (which will tunnel port 80 from the repository to port 8080 on the remote client). If you are using SSL with Apache, you can use e.g. C<80443:10.0.0.2:443>. For example, see L and after step #6, perform the following additional steps (when using svnserve): # su - localuser $ ssh -i .ssh/id_rsa remote_user@remote_host -R3690:10.0.0.2:3690 $ cd /path/to/mirror/working/copy $ svn co svn://127.0.0.1/repos/path/to/files . where 10.0.0.2 is the IP address hosting the repository service. For the same configuration when using Apache/mod_dav, do this instead: # su - localuser $ ssh -i .ssh/id_rsa remote_user@remote_host -R8080:10.0.0.2:80 $ cd /path/to/mirror/working/copy $ svn co http://127.0.0.1:8080/repos/path/to/files . =item * ssh-options If you have any other options that you would like to pass to the ssh client (for example to change the default SSH port), you can pass extra options using this parameter. Be sure that you pass it a string that has ssh long option/value pairs separated by a space, or short options without any space at all. Internally, parameter is split on spaces and passed in the @Net::SSH::options array. =back =head2 Remote Mirror Pre-requisites Before you can configure a remote mirror, you need to produce an SSH identity file to use: =over 4 =item 1. Log in as repository user Give the user identity being used to execute the hook scripts (the user running Apache or svnserve) a shell and log in as that user, e.g. C; =item 2. Create SSH identity files on repository machine Run C and create an identity file (without a password). =item 3. Log in as remote user Perform the same steps as #1, but this time on the remote machine. This username doesn't have to be the same as in step #1, but it must be a user with full write access to the mirror working copy. =item 4. Create SSH identity files on remote machine It is usually more efficient to go ahead and use C to create the .ssh folder in the home directory of the remote user. =item 5. Copy the public key from local to remote Copy the .ssh/id_dsa.pub (or id_rsa.pub if you created an RSA key) to the remote server and add it to the .ssh/authorized_keys for the remote user. See the SSH documentation for instructions on how to configure =item 6. Confirm configuration As the repository user, confirm that you can sucessfully connect to the remote account, e.g.: # su - local_user $ ssh -i .ssh/id_rsa remote_user@remote_host This is actually a good time to either check out the working copy or to confirm that the remote account has rights to update the working copy mirror. If the remote server does not have direct network access to the repository server, you can use the tunnel facility of SSH (see L above) to provide access (e.g. through a firewall). =back Once you have set up the various accounts, you are ready to set your options. =over 4 =head1 AUTHOR John Peacock =head1 COPYRIGHT Copyright (c) 2005-2008 John Peacock This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO L, L, L =cut ############################################# main pod documentation end ## SVN-Notify-Mirror-0.038/lib/SVN/Notify/Mirror.pm0000444000175000001440000002474411013430350020701 0ustar jpeacockusers#!/usr/bin/perl -w package SVN::Notify::Mirror; use base qw/SVN::Notify/; use strict; use vars qw ($VERSION); $VERSION = 0.038; __PACKAGE__->register_attributes( 'ssh_host' => 'ssh-host=s', 'ssh_user' => 'ssh-user:s', 'ssh_tunnel' => 'ssh-tunnel:s', 'ssh_identity' => 'ssh-identity:s', 'svn_binary' => 'svn-binary:s', 'tag_regex' => 'tag-regex:s', 'minimal' => 'minimal', ); sub prepare { my $self = shift; $self->prepare_recipients; $self->prepare_files; } sub execute { my ($self) = @_; return unless defined $self->to; $self->svn_binary( $ENV{SVN} || SVN::Notify->find_exe('svn') ) unless $self->svn_binary; foreach my $to ( $self->to ) { my $command = 'update'; my @args = ( -r => $self->revision, ); # need to swap function calls for backwards compatibility for now if ( defined $self->ssh_host and not $self->isa('SVN::Notify::Mirror::SSH') ) { no warnings 'redefine'; warn "Deprecated - please use SVN::Notify::Mirror::SSH directly"; require SVN::Notify::Mirror::SSH; *_cd_run = \&SVN::Notify::Mirror::SSH::_cd_run; } # deal with the possible switch case if ( defined $self->tag_regex ) { $DB::single = 1; $command = 'switch'; my $regex = $self->tag_regex; my ($tag) = grep /$regex/, @{$self->{'files'}->{'A'}}; $tag =~ s/^.+\/tags\/(.+)/$1/; return unless $tag; my @message = $self->_cd_run( $to, $self->svn_binary, 'info', ); my $URL = (split ": ", $message[1], 2)[1]; if ( $URL =~ m/^(.+\/tags\/).+$/m ) { my $url = $1; $tag = $url.$tag; } push @args, $tag; } if ( $self->minimal ) { # perform minimal update only my @paths; my $prefix = $self->{'handle_path'}; # simple case unless ( $prefix ) { # hard case $DB::single = 1; my @message = $self->_cd_run($to, $self->svn_binary, 'info'); my $URL = (split ": ", $message[1], 2)[1]; my $ROOT = (split ": ", $message[2], 2)[1]; $ROOT .= '/' unless $ROOT =~ m:/$:; ($prefix = $URL) =~ s/$ROOT//; } foreach my $files ( values %{ $self->files } ) { push @paths, map { s/$prefix// && $_ } @{ $files }; } $to .= '/'. _shortest_path(@paths); } print join("\n", $self->_cd_run( $to, $self->svn_binary, $command, @args, ) ); } } sub _cd_run { my ($self, $path, $binary, $command, @args) = @_; my @message; my $cmd ="$binary $command " . join(" ",@args); chdir ($path) or die "Couldn't CD to $path: $!"; open my $RUN, '-|', $cmd or die "Running [$cmd] failed with $?: $!"; while (<$RUN>) { chomp; push @message, $_; } close $RUN; return (@message); } sub _shortest_path { my @dirs = @_; my @shortest; DIR: foreach my $thisdir (@dirs) { my @this = split "/", $thisdir; pop @this; # either remove the filename or the last directory entry unless (@shortest) { # if we don't have anything yet @shortest = @this; next DIR; } if ( $#shortest > $#this ) { # swap the shorter path around my @temp = @shortest; @shortest = @this; @this = @temp; } while ( $shortest[$#shortest] ne $this[$#shortest] ) { # keep removing the last term until we match pop @shortest; } } return join "/", @shortest; } 1; __END__ ########################################### main pod documentation begin ## =head1 NAME SVN::Notify::Mirror - Keep a mirrored working copy of a repository path =head1 SYNOPSIS Use F in F: svnnotify --repos-path "$1" --revision "$2" \ --handler Mirror --to "/path/to/www/htdocs" \ [--svn-binary /full/path/to/svn] \ [--tag-regex "regex"] [--minimal] or better yet, use L for a more sophisticated setup: #!/usr/bin/perl -MSVN::Notify::Config=$0 --- #YAML:1.0 '': PATH: "/usr/bin:/usr/local/bin" 'path/in/repository': handler: Mirror minimal: 1 to: "/path/to/www/htdocs" 'some/other/path/in/repository': handler: Mirror to: "/path/to/remote/www/htdocs" 'some/project/tags': handler: Mirror to: "/path/to/another/dir" tag-regex: "TRUNK-" =head1 DESCRIPTION Keep a directory in sync with a portion of a Subversion repository. Typically used to keep a development web server in sync with the changes made to the repository. This directory can either be on the same box as the repository itself, or it can be remote (via SSH connection). =head1 USAGE Depending on whether the target is a L or a L, there are different options available. All options are available either as a commandline option to svnnotify or as a hash key in L (see their respective documentation for more details). =head2 Common Options In addition to all of the options available to svnnotify, there is a single global option that affects both L and L targets: =over 4 =item minimal If the C<--minimal> flag is passed on the commandline (or a hash entry created in a config file), then the update will occur at the shortest common path of all files/dirs that are changed for that revision. For example, using the example config file above, the following changes occured in rev 23: Changed paths: M path/in/repository/subdir/subdir2/file.1 A path/in/repository/subdir/file.2 M path/in/repository/subdir/subdir3/file.3 B the checked-out mirror was in C, then the update would be executed in the C directory. This is the shorted common path of the updated files. NOTE: when using the SVN::Notify::Config mode, the matching path (in the above example C) is stripped off of the shortest common path prior to appending that to the destination path (the C key). It is assumed that a matching key is the "root" of the working copy. =back =head2 Working Copy on Mirror Because 'svn export' is not able to be consistently updated, the sync'd directory must be a full working copy, and if you are running Apache, you should add lines like the following to your Apache configuration file: # Disallow browsing of Subversion working copy # administrative directories. Order deny,allow Deny from all The files in the working copy must be writeable (preferrably owned) by the user identity executing the hook script (this is the user identity that is running Apache or svnserve respectively). =head2 Local Mirror Used for directories local to the repository itself (NFS or other network mounted drives count). The only required options are: =over 4 =item * handler = Mirror Specifies that this module is called to process the Notify event. =item * to = /path/to/working/copy Specified which directory should be updated. =back =head2 Remote Mirror Used for mirrors on some other box, e.g. a web server in a DMZ network. See L or L for more details. All C options are now deprecated in the base class and support for them will be removed in the next major release. Please update your configurations (see L for details). =over 4 =head2 Methods of Mirroring There are two schemes to keep a directory synced to a path in the repository: =over 4 =item 1. Update a checked out working copy This is the normal mode of operation and is commonly used to keep a test web server in sync with the repository on every commit. =item 2. Switch a working copy to a new tag This is the preferred method when you want to keep a production web server up to date with only specific revisions, through the use of smart tagging. =back For the latter case, L can be configured to monitor a path in the repository and only trigger an update when the path matches a specific regular expression, and do so by switching the mirrored path to the new tag. =over 4 =item * tag-regex This optional parameter works with any L or L, using any of the applicable transfer methods (currently local, SSH, or Rsync). The C<--tag-regex> parameter takes a string which will be interpreted as a conventional Perl regex, and only those repository paths which match the regex will be updated. The regex also determines what the mirrored directory will be switched to. For example, using a SVN::Notify::Config file (which is the most useful way to employ this option): #!/usr/bin/perl -MSVN::Notify::Config=$0 --- #YAML:1.0 '': PATH: "/usr/bin:/usr/local/bin" 'project1/trunk': handler: Mirror to: "/path/to/test/htdocs" 'project1/tags': handler: Mirror to: "/path/to/production/htdocs" tag-regex: "TRUNK-" This would have the effect of keeping the path C in sync on every commit, but C would be switched only when a tag was created in C that matched the string C. B this is not a sophisticated regex; in particular you should not employ anchors, since the URL is not split into repos-root and path segments before testing. To initialize the switch directory, you must perform an initial checkout like this: $ svn checkout file://$REPOS/project1/tags/TRUNK-0_0_1 where C is the name of any path in the C<.../tags/> folder. =back =head1 PREREQUISITES (Optional and otherwise) The only mandatory prerequisite is SVN::Notify (obviously), but the presence of several other modules will enable other features: =over 4 =item SVN::Notify::Config Permits a YAML config file to be used as the entire postcommit script, like the second example in the L above. =item Net::SSH Required to use SSH to update a remote working copy. See L for usage of that transport method. =item File::Rsync Required to use rsync to update a remote working copy See L for usage of that transport method.. =back If you install any of these modules after installing SVN::Notify::Mirror, those features will be available immediately (though you can rerun the install in order to see the tests). =head1 AUTHOR John Peacock =head1 COPYRIGHT Copyright (c) 2005-2008 John Peacock This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =head1 SEE ALSO L, L =cut ############################################# main pod documentation end ## SVN-Notify-Mirror-0.038/README0000444000175000001440000000453111013430350015255 0ustar jpeacockusersNAME SVN::Notify::Mirror - Keep a mirrored working copy of a repository path DESCRIPTION Keep a directory in sync with a portion of a Subversion repository. Typically used to keep a development web server in sync with the changes made to the repository. This directory can either be on the same box as the repository itself, or it can be remote. NEW in version 0.038 Bugfix for Module::Build and Perl 5.10.0 NEW in version 0.037 - Support generic ssh_options parameter (for trickier settings). - Require YAML explicitly - Depends on latest SVN::Notify NOTE: Please use SVN::Notify::Mirror::SSH directly for all new installs. The compatibility feature to pass through ssh-* options will be removed in version 0.050. See the POD for more details. INSTALLATION Install Module::Build and then run: $ perl Build.PL $ ./Build $ su # ./Build install PREREQUISITES (Optional and otherwise) The only mandatory prerequisite is SVN::Notify (obviously), but the presence of several other modules will enable other features: SVN::Notify::Config Permits a YAML config file to be used as the entire postcommit script, like the second example in the SYNOPSIS. Net::SSH Required to use SSH to update a remote working copy. See SVN::Notify::Mirror::SSH for usage of that transport method. File::Rsync Required to use rsync to update a remote working copy See SVN::Notify::Mirror::RSync for usage of that transport method.. If you install any of these modules after installing SVN::Notify::Mirror, those features will be available immediately (though you can rerun the install in order to see the tests). TESTING If you have the optional Net::SSH module installed, you can test the SVN::Notify::Mirror::SSH subclass if you have the following configured: 1) a personal ssh key (located in the default ~/.ssh location); 2) your key does not have a password; 3) you have already used that key to connect successfully to localhost. Normally, once you have a personal ssh key, you can simply do $ ssh localhost and respond to the prompt to add locahost to the list of known_hosts. If this is not true, answer 'N' to the prompt during ./Build. AUTHOR John Peacock SVN-Notify-Mirror-0.038/MANIFEST.SKIP0000444000175000001440000000015111013430350016265 0ustar jpeacockusers\~$ .gz$ .bak$ .diff$ .patch$ .lwpcookies .releaserc _build ^Build$ ^Makefile$ t/recreate t/survey_repos