hackage-cli (empty) → 0.0.3.6
raw patch · 36 files changed
+4298/−0 lines, 36 filesdep +Cabaldep +aesondep +basesetup-changed
Dependencies added: Cabal, aeson, base, bytestring, containers, deepseq, directory, filepath, hackage-cli, http-io-streams, io-streams, microlens, microlens-mtl, microlens-th, mtl, netrc, optparse-applicative, pretty, process-extras, semigroups, stringsearch, tagsoup, tar, tasty, tasty-golden, text, time, unordered-containers, zlib
Files
- CHANGELOG.md +17/−0
- LICENSE +674/−0
- README.md +117/−0
- Setup.hs +2/−0
- fixtures/SVGFonts.0.1.diff +2/−0
- fixtures/SVGFonts.0.2.diff +2/−0
- fixtures/SVGFonts.0.cabal +88/−0
- fixtures/SVGFonts.1.0.diff +2/−0
- fixtures/SVGFonts.1.2.diff +5/−0
- fixtures/SVGFonts.1.cabal +92/−0
- fixtures/SVGFonts.2.0.diff +2/−0
- fixtures/SVGFonts.2.1.diff +5/−0
- fixtures/SVGFonts.2.cabal +92/−0
- fixtures/deepseq.0.1.diff +2/−0
- fixtures/deepseq.0.cabal +98/−0
- fixtures/deepseq.1.0.diff +2/−0
- fixtures/deepseq.1.cabal +100/−0
- fixtures/semigroups.0.1.diff +2/−0
- fixtures/semigroups.0.2.diff +5/−0
- fixtures/semigroups.0.cabal +173/−0
- fixtures/semigroups.1.0.diff +2/−0
- fixtures/semigroups.1.2.diff +2/−0
- fixtures/semigroups.1.cabal +176/−0
- fixtures/semigroups.2.0.diff +5/−0
- fixtures/semigroups.2.1.diff +2/−0
- fixtures/semigroups.2.cabal +173/−0
- fixtures/tree-diff.0.1.diff +13/−0
- fixtures/tree-diff.0.cabal +129/−0
- fixtures/tree-diff.1.0.diff +13/−0
- fixtures/tree-diff.1.cabal +130/−0
- hackage-cli.cabal +122/−0
- lib/Distribution/Server/Util/CabalRevisions.hs +771/−0
- src/CabalEdit.hs +98/−0
- src/IndexShaSum.hs +161/−0
- src/Main.hs +958/−0
- tests/Tests.hs +61/−0
+ CHANGELOG.md view
@@ -0,0 +1,17 @@+# Pre-release versions (February 2022)++## 0.0.3.4++Builds with `Cabal-3.4` and `GHC 9.0`.++## 0.0.3.2++Builds with `Cabal-3.2` and `GHC 8.10`.++## 0.0.3.0++Builds with `Cabal-3.0` and `GHC 8.8`.++## 0.0.2.4++Builds with `Cabal-2.4` and `GHC 8.6`.
+ LICENSE view
@@ -0,0 +1,674 @@+ GNU GENERAL PUBLIC LICENSE+ Version 3, 29 June 2007++ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.++ Preamble++ The GNU General Public License is a free, copyleft license for+software and other kinds of works.++ The licenses for most software and other practical works are designed+to take away your freedom to share and change the works. By contrast,+the GNU General Public License is intended to guarantee your freedom to+share and change all versions of a program--to make sure it remains free+software for all its users. We, the Free Software Foundation, use the+GNU General Public License for most of our software; it applies also to+any other work released this way by its authors. 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+them 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 prevent others from denying you+these rights or asking you to surrender the rights. Therefore, you have+certain responsibilities if you distribute copies of the software, or if+you modify it: responsibilities to respect the freedom of others.++ For example, if you distribute copies of such a program, whether+gratis or for a fee, you must pass on to the recipients the same+freedoms that you received. 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.++ Developers that use the GNU GPL protect your rights with two steps:+(1) assert copyright on the software, and (2) offer you this License+giving you legal permission to copy, distribute and/or modify it.++ For the developers' and authors' protection, the GPL clearly explains+that there is no warranty for this free software. For both users' and+authors' sake, the GPL requires that modified versions be marked as+changed, so that their problems will not be attributed erroneously to+authors of previous versions.++ Some devices are designed to deny users access to install or run+modified versions of the software inside them, although the manufacturer+can do so. This is fundamentally incompatible with the aim of+protecting users' freedom to change the software. The systematic+pattern of such abuse occurs in the area of products for individuals to+use, which is precisely where it is most unacceptable. Therefore, we+have designed this version of the GPL to prohibit the practice for those+products. If such problems arise substantially in other domains, we+stand ready to extend this provision to those domains in future versions+of the GPL, as needed to protect the freedom of users.++ Finally, every program is threatened constantly by software patents.+States should not allow patents to restrict development and use of+software on general-purpose computers, but in those that do, we wish to+avoid the special danger that patents applied to a free program could+make it effectively proprietary. To prevent this, the GPL assures that+patents cannot be used to render the program non-free.++ The precise terms and conditions for copying, distribution and+modification follow.++ TERMS AND CONDITIONS++ 0. Definitions.++ "This License" refers to version 3 of the GNU General Public License.++ "Copyright" also means copyright-like laws that apply to other kinds of+works, such as semiconductor masks.++ "The Program" refers to any copyrightable work licensed under this+License. Each licensee is addressed as "you". "Licensees" and+"recipients" may be individuals or organizations.++ To "modify" a work means to copy from or adapt all or part of the work+in a fashion requiring copyright permission, other than the making of an+exact copy. The resulting work is called a "modified version" of the+earlier work or a work "based on" the earlier work.++ A "covered work" means either the unmodified Program or a work based+on the Program.++ To "propagate" a work means to do anything with it that, without+permission, would make you directly or secondarily liable for+infringement under applicable copyright law, except executing it on a+computer or modifying a private copy. Propagation includes copying,+distribution (with or without modification), making available to the+public, and in some countries other activities as well.++ To "convey" a work means any kind of propagation that enables other+parties to make or receive copies. Mere interaction with a user through+a computer network, with no transfer of a copy, is not conveying.++ An interactive user interface displays "Appropriate Legal Notices"+to the extent that it includes a convenient and prominently visible+feature that (1) displays an appropriate copyright notice, and (2)+tells the user that there is no warranty for the work (except to the+extent that warranties are provided), that licensees may convey the+work under this License, and how to view a copy of this License. If+the interface presents a list of user commands or options, such as a+menu, a prominent item in the list meets this criterion.++ 1. Source Code.++ The "source code" for a work means the preferred form of the work+for making modifications to it. "Object code" means any non-source+form of a work.++ A "Standard Interface" means an interface that either is an official+standard defined by a recognized standards body, or, in the case of+interfaces specified for a particular programming language, one that+is widely used among developers working in that language.++ The "System Libraries" of an executable work include anything, other+than the work as a whole, that (a) is included in the normal form of+packaging a Major Component, but which is not part of that Major+Component, and (b) serves only to enable use of the work with that+Major Component, or to implement a Standard Interface for which an+implementation is available to the public in source code form. A+"Major Component", in this context, means a major essential component+(kernel, window system, and so on) of the specific operating system+(if any) on which the executable work runs, or a compiler used to+produce the work, or an object code interpreter used to run it.++ The "Corresponding Source" for a work in object code form means all+the source code needed to generate, install, and (for an executable+work) run the object code and to modify the work, including scripts to+control those activities. However, it does not include the work's+System Libraries, or general-purpose tools or generally available free+programs which are used unmodified in performing those activities but+which are not part of the work. For example, Corresponding Source+includes interface definition files associated with source files for+the work, and the source code for shared libraries and dynamically+linked subprograms that the work is specifically designed to require,+such as by intimate data communication or control flow between those+subprograms and other parts of the work.++ The Corresponding Source need not include anything that users+can regenerate automatically from other parts of the Corresponding+Source.++ The Corresponding Source for a work in source code form is that+same work.++ 2. Basic Permissions.++ All rights granted under this License are granted for the term of+copyright on the Program, and are irrevocable provided the stated+conditions are met. This License explicitly affirms your unlimited+permission to run the unmodified Program. The output from running a+covered work is covered by this License only if the output, given its+content, constitutes a covered work. This License acknowledges your+rights of fair use or other equivalent, as provided by copyright law.++ You may make, run and propagate covered works that you do not+convey, without conditions so long as your license otherwise remains+in force. You may convey covered works to others for the sole purpose+of having them make modifications exclusively for you, or provide you+with facilities for running those works, provided that you comply with+the terms of this License in conveying all material for which you do+not control copyright. Those thus making or running the covered works+for you must do so exclusively on your behalf, under your direction+and control, on terms that prohibit them from making any copies of+your copyrighted material outside their relationship with you.++ Conveying under any other circumstances is permitted solely under+the conditions stated below. Sublicensing is not allowed; section 10+makes it unnecessary.++ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.++ No covered work shall be deemed part of an effective technological+measure under any applicable law fulfilling obligations under article+11 of the WIPO copyright treaty adopted on 20 December 1996, or+similar laws prohibiting or restricting circumvention of such+measures.++ When you convey a covered work, you waive any legal power to forbid+circumvention of technological measures to the extent such circumvention+is effected by exercising rights under this License with respect to+the covered work, and you disclaim any intention to limit operation or+modification of the work as a means of enforcing, against the work's+users, your or third parties' legal rights to forbid circumvention of+technological measures.++ 4. Conveying Verbatim Copies.++ You may convey 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;+keep intact all notices stating that this License and any+non-permissive terms added in accord with section 7 apply to the code;+keep intact all notices of the absence of any warranty; and give all+recipients a copy of this License along with the Program.++ You may charge any price or no price for each copy that you convey,+and you may offer support or warranty protection for a fee.++ 5. Conveying Modified Source Versions.++ You may convey a work based on the Program, or the modifications to+produce it from the Program, in the form of source code under the+terms of section 4, provided that you also meet all of these conditions:++ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.++ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".++ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.++ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.++ A compilation of a covered work with other separate and independent+works, which are not by their nature extensions of the covered work,+and which are not combined with it such as to form a larger program,+in or on a volume of a storage or distribution medium, is called an+"aggregate" if the compilation and its resulting copyright are not+used to limit the access or legal rights of the compilation's users+beyond what the individual works permit. Inclusion of a covered work+in an aggregate does not cause this License to apply to the other+parts of the aggregate.++ 6. Conveying Non-Source Forms.++ You may convey a covered work in object code form under the terms+of sections 4 and 5, provided that you also convey the+machine-readable Corresponding Source under the terms of this License,+in one of these ways:++ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.++ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.++ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.++ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.++ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.++ A separable portion of the object code, whose source code is excluded+from the Corresponding Source as a System Library, need not be+included in conveying the object code work.++ A "User Product" is either (1) a "consumer product", which means any+tangible personal property which is normally used for personal, family,+or household purposes, or (2) anything designed or sold for incorporation+into a dwelling. In determining whether a product is a consumer product,+doubtful cases shall be resolved in favor of coverage. For a particular+product received by a particular user, "normally used" refers to a+typical or common use of that class of product, regardless of the status+of the particular user or of the way in which the particular user+actually uses, or expects or is expected to use, the product. A product+is a consumer product regardless of whether the product has substantial+commercial, industrial or non-consumer uses, unless such uses represent+the only significant mode of use of the product.++ "Installation Information" for a User Product means any methods,+procedures, authorization keys, or other information required to install+and execute modified versions of a covered work in that User Product from+a modified version of its Corresponding Source. The information must+suffice to ensure that the continued functioning of the modified object+code is in no case prevented or interfered with solely because+modification has been made.++ If you convey an object code work under this section in, or with, or+specifically for use in, a User Product, and the conveying occurs as+part of a transaction in which the right of possession and use of the+User Product is transferred to the recipient in perpetuity or for a+fixed term (regardless of how the transaction is characterized), the+Corresponding Source conveyed under this section must be accompanied+by the Installation Information. But this requirement does not apply+if neither you nor any third party retains the ability to install+modified object code on the User Product (for example, the work has+been installed in ROM).++ The requirement to provide Installation Information does not include a+requirement to continue to provide support service, warranty, or updates+for a work that has been modified or installed by the recipient, or for+the User Product in which it has been modified or installed. Access to a+network may be denied when the modification itself materially and+adversely affects the operation of the network or violates the rules and+protocols for communication across the network.++ Corresponding Source conveyed, and Installation Information provided,+in accord with this section must be in a format that is publicly+documented (and with an implementation available to the public in+source code form), and must require no special password or key for+unpacking, reading or copying.++ 7. Additional Terms.++ "Additional permissions" are terms that supplement the terms of this+License by making exceptions from one or more of its conditions.+Additional permissions that are applicable to the entire Program shall+be treated as though they were included in this License, to the extent+that they are valid under applicable law. If additional permissions+apply only to part of the Program, that part may be used separately+under those permissions, but the entire Program remains governed by+this License without regard to the additional permissions.++ When you convey a copy of a covered work, you may at your option+remove any additional permissions from that copy, or from any part of+it. (Additional permissions may be written to require their own+removal in certain cases when you modify the work.) You may place+additional permissions on material, added by you to a covered work,+for which you have or can give appropriate copyright permission.++ Notwithstanding any other provision of this License, for material you+add to a covered work, you may (if authorized by the copyright holders of+that material) supplement the terms of this License with terms:++ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or++ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or++ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or++ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or++ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or++ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.++ All other non-permissive additional terms are considered "further+restrictions" within the meaning of section 10. If the Program as you+received it, or any part of it, contains a notice stating that it is+governed by this License along with a term that is a further+restriction, you may remove that term. If a license document contains+a further restriction but permits relicensing or conveying under this+License, you may add to a covered work material governed by the terms+of that license document, provided that the further restriction does+not survive such relicensing or conveying.++ If you add terms to a covered work in accord with this section, you+must place, in the relevant source files, a statement of the+additional terms that apply to those files, or a notice indicating+where to find the applicable terms.++ Additional terms, permissive or non-permissive, may be stated in the+form of a separately written license, or stated as exceptions;+the above requirements apply either way.++ 8. Termination.++ You may not propagate or modify a covered work except as expressly+provided under this License. Any attempt otherwise to propagate or+modify it is void, and will automatically terminate your rights under+this License (including any patent licenses granted under the third+paragraph of section 11).++ However, if you cease all violation of this License, then your+license from a particular copyright holder is reinstated (a)+provisionally, unless and until the copyright holder explicitly and+finally terminates your license, and (b) permanently, if the copyright+holder fails to notify you of the violation by some reasonable means+prior to 60 days after the cessation.++ Moreover, your license from a particular copyright holder is+reinstated permanently if the copyright holder notifies you of the+violation by some reasonable means, this is the first time you have+received notice of violation of this License (for any work) from that+copyright holder, and you cure the violation prior to 30 days after+your receipt of the notice.++ Termination of your rights under this section does not terminate the+licenses of parties who have received copies or rights from you under+this License. If your rights have been terminated and not permanently+reinstated, you do not qualify to receive new licenses for the same+material under section 10.++ 9. Acceptance Not Required for Having Copies.++ You are not required to accept this License in order to receive or+run a copy of the Program. Ancillary propagation of a covered work+occurring solely as a consequence of using peer-to-peer transmission+to receive a copy likewise does not require acceptance. However,+nothing other than this License grants you permission to propagate or+modify any covered work. These actions infringe copyright if you do+not accept this License. Therefore, by modifying or propagating a+covered work, you indicate your acceptance of this License to do so.++ 10. Automatic Licensing of Downstream Recipients.++ Each time you convey a covered work, the recipient automatically+receives a license from the original licensors, to run, modify and+propagate that work, subject to this License. You are not responsible+for enforcing compliance by third parties with this License.++ An "entity transaction" is a transaction transferring control of an+organization, or substantially all assets of one, or subdividing an+organization, or merging organizations. If propagation of a covered+work results from an entity transaction, each party to that+transaction who receives a copy of the work also receives whatever+licenses to the work the party's predecessor in interest had or could+give under the previous paragraph, plus a right to possession of the+Corresponding Source of the work from the predecessor in interest, if+the predecessor has it or can get it with reasonable efforts.++ You may not impose any further restrictions on the exercise of the+rights granted or affirmed under this License. For example, you may+not impose a license fee, royalty, or other charge for exercise of+rights granted under this License, and you may not initiate litigation+(including a cross-claim or counterclaim in a lawsuit) alleging that+any patent claim is infringed by making, using, selling, offering for+sale, or importing the Program or any portion of it.++ 11. Patents.++ A "contributor" is a copyright holder who authorizes use under this+License of the Program or a work on which the Program is based. The+work thus licensed is called the contributor's "contributor version".++ A contributor's "essential patent claims" are all patent claims+owned or controlled by the contributor, whether already acquired or+hereafter acquired, that would be infringed by some manner, permitted+by this License, of making, using, or selling its contributor version,+but do not include claims that would be infringed only as a+consequence of further modification of the contributor version. For+purposes of this definition, "control" includes the right to grant+patent sublicenses in a manner consistent with the requirements of+this License.++ Each contributor grants you a non-exclusive, worldwide, royalty-free+patent license under the contributor's essential patent claims, to+make, use, sell, offer for sale, import and otherwise run, modify and+propagate the contents of its contributor version.++ In the following three paragraphs, a "patent license" is any express+agreement or commitment, however denominated, not to enforce a patent+(such as an express permission to practice a patent or covenant not to+sue for patent infringement). To "grant" such a patent license to a+party means to make such an agreement or commitment not to enforce a+patent against the party.++ If you convey a covered work, knowingly relying on a patent license,+and the Corresponding Source of the work is not available for anyone+to copy, free of charge and under the terms of this License, through a+publicly available network server or other readily accessible means,+then you must either (1) cause the Corresponding Source to be so+available, or (2) arrange to deprive yourself of the benefit of the+patent license for this particular work, or (3) arrange, in a manner+consistent with the requirements of this License, to extend the patent+license to downstream recipients. "Knowingly relying" means you have+actual knowledge that, but for the patent license, your conveying the+covered work in a country, or your recipient's use of the covered work+in a country, would infringe one or more identifiable patents in that+country that you have reason to believe are valid.++ If, pursuant to or in connection with a single transaction or+arrangement, you convey, or propagate by procuring conveyance of, a+covered work, and grant a patent license to some of the parties+receiving the covered work authorizing them to use, propagate, modify+or convey a specific copy of the covered work, then the patent license+you grant is automatically extended to all recipients of the covered+work and works based on it.++ A patent license is "discriminatory" if it does not include within+the scope of its coverage, prohibits the exercise of, or is+conditioned on the non-exercise of one or more of the rights that are+specifically granted under this License. You may not convey a covered+work if you are a party to an arrangement with a third party that is+in the business of distributing software, under which you make payment+to the third party based on the extent of your activity of conveying+the work, and under which the third party grants, to any of the+parties who would receive the covered work from you, a discriminatory+patent license (a) in connection with copies of the covered work+conveyed by you (or copies made from those copies), or (b) primarily+for and in connection with specific products or compilations that+contain the covered work, unless you entered into that arrangement,+or that patent license was granted, prior to 28 March 2007.++ Nothing in this License shall be construed as excluding or limiting+any implied license or other defenses to infringement that may+otherwise be available to you under applicable patent law.++ 12. No Surrender of Others' Freedom.++ If 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 convey a+covered work so as to satisfy simultaneously your obligations under this+License and any other pertinent obligations, then as a consequence you may+not convey it at all. For example, if you agree to terms that obligate you+to collect a royalty for further conveying from those to whom you convey+the Program, the only way you could satisfy both those terms and this+License would be to refrain entirely from conveying the Program.++ 13. Use with the GNU Affero General Public License.++ Notwithstanding any other provision of this License, you have+permission to link or combine any covered work with a work licensed+under version 3 of the GNU Affero General Public License into a single+combined work, and to convey the resulting work. The terms of this+License will continue to apply to the part which is the covered work,+but the special requirements of the GNU Affero General Public License,+section 13, concerning interaction through a network will apply to the+combination as such.++ 14. Revised Versions of this License.++ The Free Software Foundation may publish revised and/or new versions of+the GNU 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 that a certain numbered version of the GNU General+Public License "or any later version" applies to it, you have the+option of following the terms and conditions either of that numbered+version or of any later version published by the Free Software+Foundation. If the Program does not specify a version number of the+GNU General Public License, you may choose any version ever published+by the Free Software Foundation.++ If the Program specifies that a proxy can decide which future+versions of the GNU General Public License can be used, that proxy's+public statement of acceptance of a version permanently authorizes you+to choose that version for the Program.++ Later license versions may give you additional or different+permissions. However, no additional obligations are imposed on any+author or copyright holder as a result of your choosing to follow a+later version.++ 15. Disclaimer of Warranty.++ 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.++ 16. Limitation of Liability.++ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+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.++ 17. Interpretation of Sections 15 and 16.++ If the disclaimer of warranty and limitation of liability provided+above cannot be given local legal effect according to their terms,+reviewing courts shall apply local law that most closely approximates+an absolute waiver of all civil liability in connection with the+Program, unless a warranty or assumption of liability accompanies a+copy of the Program in return for a fee.++ END OF TERMS AND CONDITIONS++ How to Apply These Terms to Your New Programs++ If you develop a new program, and you want it to be of the greatest+possible use to the public, the best way to achieve this is to make it+free software which everyone can redistribute and change under these terms.++ To do so, attach the following notices to the program. It is safest+to attach them to the start of each source file to most effectively+state the exclusion of warranty; and each file should have at least+the "copyright" line and a pointer to where the full notice is found.++ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>++ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU General Public License as published by+ the Free Software Foundation, either version 3 of the License, or+ (at your option) any later version.++ This program is distributed in the hope that it will be useful,+ but WITHOUT ANY WARRANTY; without even the implied warranty of+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the+ GNU General Public License for more details.++ You should have received a copy of the GNU General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.++Also add information on how to contact you by electronic and paper mail.++ If the program does terminal interaction, make it output a short+notice like this when it starts in an interactive mode:++ <program> Copyright (C) <year> <name of author>+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.+ This is free software, and you are welcome to redistribute it+ under certain conditions; type `show c' for details.++The hypothetical commands `show w' and `show c' should show the appropriate+parts of the General Public License. Of course, your program's commands+might be different; for a GUI interface, you would use an "about box".++ You should also get your employer (if you work as a programmer) or school,+if any, to sign a "copyright disclaimer" for the program, if necessary.+For more information on this, and how to apply and follow the GNU GPL, see+<http://www.gnu.org/licenses/>.++ The GNU General Public License does not permit incorporating your program+into proprietary programs. If your program is a subroutine library, you+may consider it more useful to permit linking proprietary applications with+the library. If this is what you want to do, use the GNU Lesser General+Public License instead of this License. But first, please read+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+ README.md view
@@ -0,0 +1,117 @@+# `hackage-cli`++A command-line tool to manage package metadata+[revisions](https://github.com/haskell-infra/hackage-trustees/blob/master/revisions-information.md)+on Hackage.++Caveat: this is a developer tool and work-in-progress.+Check known bugs at the [issue tracker](https://github.com/hackage-trustees/hackage-cli/issues).++## Command-line reference++```+hackage-cli - CLI tool for Hackage++Usage: hackage-cli [--version] [--verbose] [--hostname HOSTNAME] COMMAND++Available options:+ -h,--help Show this help text+ --version output version information and exit+ --verbose enable verbose output+ --hostname HOSTNAME Hackage hostname (default: "hackage.haskell.org")++Available commands:+ pull-cabal download .cabal files for a package+ push-cabal upload revised .cabal files+ sync-cabal upadate/sync local .cabal file with latest revision+ on Hackage+ push-candidate upload package candidate(s)+ list-versions list versions for a package+ check-revision validate revision+ index-sha256sum generate sha256sum-format file+ add-bound add bound to the library section of a package. .cabal+ file is edited in place++Each command has a sub-`--help` text. Hackage credentials are expected to be+stored in an `${HOME}/.netrc`-entry for the respective Hackage hostname. E.g.+"machine hackage.haskell.org login MyUserName password TrustNo1". All+interactions with Hackage occur TLS-encrypted via the HTTPS protocol.+```++(Section created 2015-04-26, last updated 2018-03-21.)++## How to: bulk revision++Suppose that all versions starting with `1.2.3` of the package `pkg-x`+on hackage need the additional bound `< 4.5.6` on their dependency+`pkg-y`, typically to prevent compilation attempts with `pkg-y-4.5.6`+and up that will fail.++With `hackage-cli` this can be done in a typical workflow that consists of++ 1. downloading the `.cabal` files,+ 2. adding bounds (with `hackage-cli`), or making other revisions (manually),+ 3. re-uploading the modified `.cabal` files.++We walk through a typical workflow:++1. Download the relevant `pkg-x.cabal` files to a new temporary directory:+ ```+ $ cd $(mktemp)+ $ hackage-cli pull-cabal pkg-x '>= 1.2.3'+ ```++2. Adding bound `pkg-y < 4.5.6` to all of them:+ ```+ $ hackage-cli add-bound pkg-y '< 4.5.6' *.cabal+ ```+ This will write a new line+ ```+ build-depends: pkg-y < 4.5.6+ ```+ to the `library` section of each of the `.cabal` files.+ There must be exactly one `library` section, otherwise `hackage-cli` will crash+ or produce a garbage result.++ If this bound does not further constrain the existing version range+ for `pkg-y`, it will not be added unless `--force` is used.+ (E.g., one of the cabal files could already have a bound `pkg-y <+ 3.0.0` or so---this file will remain unchanged.)++ You can of course add more bounds using `add-bound` or make further+ manual changes to `.cabal` files.++3. Review the changes via a trial upload:+ ```+ $ hackage-cli push-cabal --incr-rev *.cabal+ ```+ (The flag `--incr-rev` will increment the `x-revision` field by `1` during the upload.+ Existing revisions cannot be overwritten.)++ During (trial) upload you will see for each `.cabal` file a summary of what will be changed.+ Hackage might reject your revision, if it does not match the criteria+ of what it thinks is+ [legal](https://github.com/haskell-infra/hackage-trustees/blob/master/revisions-information.md).+ Note that the legality check of the Hackage server is neither+ complete nor sound, so some legal revisions might be rejected, and some illegal ones accepted.+ You bear the responsibility for correct revisions yourself.++4. Upload!+ ```+ $ hackage-cli push-cabal --incr-rev --publish *.cabal+ ```+ Adding the flag `--publish` will actually commit the revisions to Hackage.++(Section created 2022-02-21.)++## License++- Licensed under GPL-3.++- (C) 2015 Herbert Valerio Riedel.+- (C) 2016-2019 Herbert Valerio Riedel and Oleg Grenrus.+- (C) 2021-2022 Andreas Abel.++- Further contributors: Simon Jakobi, Kevin Buhr.++(Section created 2022-02-21.)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ fixtures/SVGFonts.0.1.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/SVGFonts.0.2.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/SVGFonts.0.cabal view
@@ -0,0 +1,88 @@+Name: SVGFonts+Version: 1.6.0.3+Synopsis: Fonts from the SVG-Font format+Description: Native font support for the diagrams framework (<http://projects.haskell.org/diagrams/>). Note that this package can be used with any diagrams backend, not just the SVG backend. The SVG-font format is easy to parse+ and was therefore chosen for a font library completely written in Haskell.+ .+ You can convert your own font to SVG with <http://fontforge.github.io/>,+ or use the included LinLibertine and Bitstream fonts.+ .+ Features:+ .+ * Complete implementation of the features that Fontforge produces (though not the complete SVG format)+ .+ * Kerning (/i.e./ the two characters in \"VA\" are closer than the characters in \"VV\")+ .+ * Unicode+ .+ * Ligatures+ .+ * An example that shows how to do text boxes with syntax highlighting using highlighting-kate:+ <http://hackage.haskell.org/package/highlighting-kate>+ .+ XML speed issues can be solved by trimming the svg file to only those characters that are used (or maybe binary xml one day).+ .+ Version 1.0 of this library supports texturing, though this only makes sense in a diagrams backend that does rasterization in Haskell, such as diagrams-rasterific.+ .+ Example:+ .+ > # LANGUAGE NoMonomorphismRestriction #+ >+ > import Diagrams.Prelude+ > import Diagrams.Backend.Rasterific.CmdLine+ > import Graphics.SVGFonts+ >+ > main = defaultMain (text' "Hello World")+ >+ > text' t = stroke (textSVG t 1) # fc purple # fillRule EvenOdd+ > text'' t = stroke (textSVG' (TextOpts lin INSIDE_H KERN False 1 1) t) # fillRule EvenOdd+ > text''' t = (textSVG_ (TextOpts lin INSIDE_H KERN True 1 1) t) # fillRule EvenOdd+ .+category: Graphics+License: BSD3+License-file: LICENSE+Author: Tillmann Vogt+Maintainer: diagrams-discuss@googlegroups.com+build-type: Simple+Cabal-Version: >=1.18++data-files: fonts/*.svg+extra-source-files: CHANGES.md, README.md, diagrams/*.svg+extra-doc-files: diagrams/*.svg+Tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2, GHC == 8.0.1, GHC == 8.2.1+source-repository head+ type: git+ location: https://github.com/diagrams/SVGFonts.git++Library+ hs-source-dirs: src+ ghc-options: -W -Wall -fno-warn-unused-do-bind+ other-modules: Paths_SVGFonts+ build-depends:+ attoparsec,+ base == 4.*,+ bytestring >= 0.10 && < 1.0,+ cereal,+ cereal-vector,+ containers >= 0.4 && < 0.6,+ data-default-class < 0.2,+ diagrams-core >= 1.3 && < 1.5,+ diagrams-lib >= 1.3 && < 1.5,+ directory >= 1.1,+ blaze-svg >= 0.3.3,+ blaze-markup >= 0.5,+ parsec,+ split,+ text,+ tuple,+ vector,+ xml+ exposed-modules:+ Graphics.SVGFonts+ Graphics.SVGFonts.Text+ Graphics.SVGFonts.ReadFont+ Graphics.SVGFonts.Fonts+ Graphics.SVGFonts.CharReference+ Graphics.SVGFonts.ReadPath+ Graphics.SVGFonts.WriteFont+ default-language: Haskell2010
+ fixtures/SVGFonts.1.0.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/SVGFonts.1.2.diff view
@@ -0,0 +1,5 @@+OK+Normal+the library component's library dependency on 'diagrams-lib'+- <1.4++ >=0
+ fixtures/SVGFonts.1.cabal view
@@ -0,0 +1,92 @@+Name: SVGFonts+Version: 1.6.0.3+x-revision: 1+Synopsis: Fonts from the SVG-Font format+Description: Native font support for the diagrams framework (<http://projects.haskell.org/diagrams/>). Note that this package can be used with any diagrams backend, not just the SVG backend. The SVG-font format is easy to parse+ and was therefore chosen for a font library completely written in Haskell.+ .+ You can convert your own font to SVG with <http://fontforge.github.io/>,+ or use the included LinLibertine and Bitstream fonts.+ .+ Features:+ .+ * Complete implementation of the features that Fontforge produces (though not the complete SVG format)+ .+ * Kerning (/i.e./ the two characters in \"VA\" are closer than the characters in \"VV\")+ .+ * Unicode+ .+ * Ligatures+ .+ * An example that shows how to do text boxes with syntax highlighting using highlighting-kate:+ <http://hackage.haskell.org/package/highlighting-kate>+ .+ XML speed issues can be solved by trimming the svg file to only those characters that are used (or maybe binary xml one day).+ .+ Version 1.0 of this library supports texturing, though this only makes sense in a diagrams backend that does rasterization in Haskell, such as diagrams-rasterific.+ .+ Example:+ .+ > # LANGUAGE NoMonomorphismRestriction #+ >+ > import Diagrams.Prelude+ > import Diagrams.Backend.Rasterific.CmdLine+ > import Graphics.SVGFonts+ >+ > main = defaultMain (text' "Hello World")+ >+ > text' t = stroke (textSVG t 1) # fc purple # fillRule EvenOdd+ > text'' t = stroke (textSVG' (TextOpts lin INSIDE_H KERN False 1 1) t) # fillRule EvenOdd+ > text''' t = (textSVG_ (TextOpts lin INSIDE_H KERN True 1 1) t) # fillRule EvenOdd+ .+category: Graphics+License: BSD3+License-file: LICENSE+Author: Tillmann Vogt+Maintainer: diagrams-discuss@googlegroups.com+build-type: Simple+Cabal-Version: >=1.18++data-files: fonts/*.svg+extra-source-files: CHANGES.md, README.md, diagrams/*.svg+extra-doc-files: diagrams/*.svg+Tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2, GHC == 8.0.1, GHC == 8.2.1+source-repository head+ type: git+ location: https://github.com/diagrams/SVGFonts.git++Library+ hs-source-dirs: src+ ghc-options: -W -Wall -fno-warn-unused-do-bind+ other-modules: Paths_SVGFonts+ build-depends:+ attoparsec,+ base == 4.*,+ bytestring >= 0.10 && < 1.0,+ cereal,+ cereal-vector,+ containers >= 0.4 && < 0.6,+ data-default-class < 0.2,+ diagrams-core >= 1.3 && < 1.5,+ diagrams-lib >= 1.3 && < 1.5,+ directory >= 1.1,+ blaze-svg >= 0.3.3,+ blaze-markup >= 0.5,+ parsec,+ split,+ text,+ tuple,+ vector,+ xml+ exposed-modules:+ Graphics.SVGFonts+ Graphics.SVGFonts.Text+ Graphics.SVGFonts.ReadFont+ Graphics.SVGFonts.Fonts+ Graphics.SVGFonts.CharReference+ Graphics.SVGFonts.ReadPath+ Graphics.SVGFonts.WriteFont+ default-language: Haskell2010++ if impl(ghc == 7.8.*)+ build-depends: diagrams-lib < 1.4
+ fixtures/SVGFonts.2.0.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/SVGFonts.2.1.diff view
@@ -0,0 +1,5 @@+OK+Normal+the library component's library dependency on 'diagrams-lib'+- >=0++ <1.4
+ fixtures/SVGFonts.2.cabal view
@@ -0,0 +1,92 @@+Name: SVGFonts+Version: 1.6.0.3+x-revision: 2+Synopsis: Fonts from the SVG-Font format+Description: Native font support for the diagrams framework (<http://projects.haskell.org/diagrams/>). Note that this package can be used with any diagrams backend, not just the SVG backend. The SVG-font format is easy to parse+ and was therefore chosen for a font library completely written in Haskell.+ .+ You can convert your own font to SVG with <http://fontforge.github.io/>,+ or use the included LinLibertine and Bitstream fonts.+ .+ Features:+ .+ * Complete implementation of the features that Fontforge produces (though not the complete SVG format)+ .+ * Kerning (/i.e./ the two characters in \"VA\" are closer than the characters in \"VV\")+ .+ * Unicode+ .+ * Ligatures+ .+ * An example that shows how to do text boxes with syntax highlighting using highlighting-kate:+ <http://hackage.haskell.org/package/highlighting-kate>+ .+ XML speed issues can be solved by trimming the svg file to only those characters that are used (or maybe binary xml one day).+ .+ Version 1.0 of this library supports texturing, though this only makes sense in a diagrams backend that does rasterization in Haskell, such as diagrams-rasterific.+ .+ Example:+ .+ > # LANGUAGE NoMonomorphismRestriction #+ >+ > import Diagrams.Prelude+ > import Diagrams.Backend.Rasterific.CmdLine+ > import Graphics.SVGFonts+ >+ > main = defaultMain (text' "Hello World")+ >+ > text' t = stroke (textSVG t 1) # fc purple # fillRule EvenOdd+ > text'' t = stroke (textSVG' (TextOpts lin INSIDE_H KERN False 1 1) t) # fillRule EvenOdd+ > text''' t = (textSVG_ (TextOpts lin INSIDE_H KERN True 1 1) t) # fillRule EvenOdd+ .+category: Graphics+License: BSD3+License-file: LICENSE+Author: Tillmann Vogt+Maintainer: diagrams-discuss@googlegroups.com+build-type: Simple+Cabal-Version: >=1.18++data-files: fonts/*.svg+extra-source-files: CHANGES.md, README.md, diagrams/*.svg+extra-doc-files: diagrams/*.svg+Tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.2, GHC == 8.0.1, GHC == 8.2.1+source-repository head+ type: git+ location: https://github.com/diagrams/SVGFonts.git++Library+ hs-source-dirs: src+ ghc-options: -W -Wall -fno-warn-unused-do-bind+ other-modules: Paths_SVGFonts+ build-depends:+ attoparsec,+ base == 4.*,+ bytestring >= 0.10 && < 1.0,+ cereal,+ cereal-vector,+ containers >= 0.4 && < 0.6,+ data-default-class < 0.2,+ diagrams-core >= 1.3 && < 1.5,+ diagrams-lib >= 1.3 && < 1.5,+ directory >= 1.1,+ blaze-svg >= 0.3.3,+ blaze-markup >= 0.5,+ parsec,+ split,+ text,+ tuple,+ vector,+ xml+ exposed-modules:+ Graphics.SVGFonts+ Graphics.SVGFonts.Text+ Graphics.SVGFonts.ReadFont+ Graphics.SVGFonts.Fonts+ Graphics.SVGFonts.CharReference+ Graphics.SVGFonts.ReadPath+ Graphics.SVGFonts.WriteFont+ default-language: Haskell2010++ if impl(ghc == 7.8.*)+ build-depends: diagrams-lib -any
+ fixtures/deepseq.0.1.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add new library dependency on 'text' in library component
+ fixtures/deepseq.0.cabal view
@@ -0,0 +1,98 @@+cabal-version: 1.12+name: deepseq+version: 1.4.4.0+-- NOTE: Don't forget to update ./changelog.md++license: BSD3+license-file: LICENSE+maintainer: libraries@haskell.org+bug-reports: https://github.com/haskell/deepseq/issues+synopsis: Deep evaluation of data structures+category: Control+description:+ This package provides methods for fully evaluating data structures+ (\"deep evaluation\"). Deep evaluation is often used for adding+ strictness to a program, e.g. in order to force pending exceptions,+ remove space leaks, or force lazy I/O to happen. It is also useful+ in parallel programs, to ensure pending work does not migrate to the+ wrong thread.+ .+ The primary use of this package is via the 'deepseq' function, a+ \"deep\" version of 'seq'. It is implemented on top of an 'NFData'+ typeclass (\"Normal Form Data\", data structures with no unevaluated+ components) which defines strategies for fully evaluating different+ data types. See module documentation in "Control.DeepSeq" for more+ details.++build-type: Simple+tested-with: GHC==8.6.1,+ GHC==8.4.3, GHC==8.4.2, GHC==8.4.1,+ GHC==8.2.2, GHC==8.2.1,+ GHC==8.0.2, GHC==8.0.1,+ GHC==7.10.3, GHC==7.10.2, GHC==7.10.1,+ GHC==7.8.4, GHC==7.8.3, GHC==7.8.2, GHC==7.8.1,+ GHC==7.6.3, GHC==7.6.2, GHC==7.6.1,+ GHC==7.4.2, GHC==7.4.1++extra-source-files: changelog.md++source-repository head+ type: git+ location: https://github.com/haskell/deepseq.git++library+ default-language: Haskell2010+ other-extensions:+ BangPatterns+ CPP+ DefaultSignatures+ FlexibleContexts+ FlexibleInstances+ GADTs+ MultiParamTypeClasses+ Safe+ TypeOperators++ -- GHC.Generics lived in `ghc-prim` for GHC 7.2 & GHC 7.4+ if impl(ghc == 7.4.*)+ build-depends: ghc-prim == 0.2.*++ if impl(ghc>=7.6)+ other-extensions: PolyKinds++ if impl(ghc>=7.8)+ other-extensions: EmptyCase++ build-depends: base >= 4.5 && < 4.13,+ array >= 0.4 && < 0.6+ ghc-options: -Wall++ exposed-modules: Control.DeepSeq+ other-modules: Control.DeepSeq.BackDoor++test-suite deepseq-generics-tests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: . tests+ main-is: Main.hs+ other-extensions:+ CPP+ BangPatterns+ DefaultSignatures+ DeriveDataTypeable+ DeriveGeneric+ FlexibleContexts+ Safe+ TupleSections+ TypeOperators++ ghc-options: -Wall++ build-depends:+ array,+ base,+ ghc-prim,+ -- end of packages with inherited version constraints+ test-framework == 0.8.*,+ test-framework-hunit == 0.3.*,+ HUnit >= 1.2 && < 1.7
+ fixtures/deepseq.1.0.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot remove existing library dependency on 'text' in library component
+ fixtures/deepseq.1.cabal view
@@ -0,0 +1,100 @@+cabal-version: 1.12+name: deepseq+version: 1.4.4.0+x-revision: 1+-- NOTE: Don't forget to update ./changelog.md++license: BSD3+license-file: LICENSE+maintainer: libraries@haskell.org+bug-reports: https://github.com/haskell/deepseq/issues+synopsis: Deep evaluation of data structures+category: Control+description:+ This package provides methods for fully evaluating data structures+ (\"deep evaluation\"). Deep evaluation is often used for adding+ strictness to a program, e.g. in order to force pending exceptions,+ remove space leaks, or force lazy I/O to happen. It is also useful+ in parallel programs, to ensure pending work does not migrate to the+ wrong thread.+ .+ The primary use of this package is via the 'deepseq' function, a+ \"deep\" version of 'seq'. It is implemented on top of an 'NFData'+ typeclass (\"Normal Form Data\", data structures with no unevaluated+ components) which defines strategies for fully evaluating different+ data types. See module documentation in "Control.DeepSeq" for more+ details.++build-type: Simple+tested-with: GHC==8.6.1,+ GHC==8.4.3, GHC==8.4.2, GHC==8.4.1,+ GHC==8.2.2, GHC==8.2.1,+ GHC==8.0.2, GHC==8.0.1,+ GHC==7.10.3, GHC==7.10.2, GHC==7.10.1,+ GHC==7.8.4, GHC==7.8.3, GHC==7.8.2, GHC==7.8.1,+ GHC==7.6.3, GHC==7.6.2, GHC==7.6.1,+ GHC==7.4.2, GHC==7.4.1++extra-source-files: changelog.md++source-repository head+ type: git+ location: https://github.com/haskell/deepseq.git++library+ default-language: Haskell2010+ other-extensions:+ BangPatterns+ CPP+ DefaultSignatures+ FlexibleContexts+ FlexibleInstances+ GADTs+ MultiParamTypeClasses+ Safe+ TypeOperators++ -- GHC.Generics lived in `ghc-prim` for GHC 7.2 & GHC 7.4+ if impl(ghc == 7.4.*)+ build-depends: ghc-prim == 0.2.*++ if impl(ghc>=7.6)+ other-extensions: PolyKinds++ if impl(ghc>=7.8)+ other-extensions: EmptyCase++ build-depends: base >= 4.5 && < 4.13,+ array >= 0.4 && < 0.6,+ text >= 1.2.3.0 && <1.4+ ghc-options: -Wall++ exposed-modules: Control.DeepSeq+ other-modules: Control.DeepSeq.BackDoor++test-suite deepseq-generics-tests+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ hs-source-dirs: . tests+ main-is: Main.hs+ other-extensions:+ CPP+ BangPatterns+ DefaultSignatures+ DeriveDataTypeable+ DeriveGeneric+ FlexibleContexts+ Safe+ TupleSections+ TypeOperators++ ghc-options: -Wall++ build-depends:+ array,+ base,+ ghc-prim,+ -- end of packages with inherited version constraints+ test-framework == 0.8.*,+ test-framework-hunit == 0.3.*,+ HUnit >= 1.2 && < 1.7
+ fixtures/semigroups.0.1.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/semigroups.0.2.diff view
@@ -0,0 +1,5 @@+OK+Normal+the library component's library dependency on 'containers'+- >=0.3 && <0.7++ >=0.3 && <0.8
+ fixtures/semigroups.0.cabal view
@@ -0,0 +1,173 @@+name: semigroups+category: Algebra, Data, Data Structures, Math+version: 0.18.5+license: BSD3+cabal-version: >= 1.10+license-file: LICENSE+author: Edward A. Kmett+maintainer: Edward A. Kmett <ekmett@gmail.com>+stability: provisional+homepage: http://github.com/ekmett/semigroups/+bug-reports: http://github.com/ekmett/semigroups/issues+copyright: Copyright (C) 2011-2015 Edward A. Kmett+synopsis: Anything that associates+description:+ In mathematics, a semigroup is an algebraic structure consisting of a set together with an associative binary operation. A semigroup generalizes a monoid in that there might not exist an identity element. It also (originally) generalized a group (a monoid with all inverses) to a type where every element did not have to have an inverse, thus the name semigroup.+build-type: Simple+extra-source-files: .travis.yml README.markdown CHANGELOG.markdown+tested-with: GHC == 7.0.4+ , GHC == 7.2.2+ , GHC == 7.4.2+ , GHC == 7.6.3+ , GHC == 7.8.4+ , GHC == 7.10.3+ , GHC == 8.0.2+ , GHC == 8.2.2+ , GHC == 8.4.3+ , GHC == 8.6.1++source-repository head+ type: git+ location: git://github.com/ekmett/semigroups.git++flag hashable+ description:+ You can disable the use of the `hashable` package using `-f-hashable`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ .+ If disabled we will not supply instances of `Hashable`+ .+ Note: `-f-hashable` implies `-f-unordered-containers`, as we are necessarily not able to supply those instances as well.+ default: True+ manual: True++flag binary+ description:+ You can disable the use of the `binary` package using `-f-binary`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring+ description:+ You can disable the use of the `bytestring` package using `-f-bytestring`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring-builder+ description:+ Decides whether to use an older version of bytestring along with bytestring-builder or just a newer version of bytestring.+ .+ This flag normally toggles automatically but you can use `-fbytestring-builder` or `-f-bytestring-builder` to explicitly change it.+ default: False+ manual: False++flag containers+ description:+ You can disable the use of the `containers` package using `-f-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag deepseq+ description:+ You can disable the use of the `deepseq` package using `-f-deepseq`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag tagged+ description:+ You can disable the use of the `tagged` package using `-f-tagged`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag text+ description:+ You can disable the use of the `text` package using `-f-text`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag transformers+ description:+ You can disable the use of the `transformers` and `transformers-compat` packages using `-f-transformers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag unordered-containers+ description:+ You can disable the use of the `unordered-containers` package using `-f-unordered-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++library+ default-language: Haskell98+ hs-source-dirs: src+ ghc-options: -Wall++ build-depends: base >= 2 && < 5++ if impl(ghc >= 7.2)+ exposed-modules:+ Data.Semigroup.Generic++ -- legacy configuration+ if impl(ghc < 7.11.20151002)+ -- starting with GHC 8 these modules are provided by `base`+ hs-source-dirs: src-ghc7+ exposed-modules:+ Data.Semigroup+ Data.List.NonEmpty++ -- Not needed anymore since GHC 7.10+ if impl(ghc < 7.10)+ build-depends: nats >= 0.1 && < 2++ if impl(ghc >= 7.2 && < 7.5)+ build-depends: ghc-prim++ if flag(binary)+ build-depends: binary++ if flag(bytestring)+ if flag(bytestring-builder)+ build-depends: bytestring >= 0.9 && < 0.10.4,+ bytestring-builder >= 0.10.4 && < 1+ else+ build-depends: bytestring >= 0.10.4 && < 1++ if flag(containers)+ build-depends: containers >= 0.3 && < 0.7++ if flag(deepseq)+ build-depends: deepseq >= 1.1 && < 1.5++ if flag(tagged)+ build-depends: tagged >= 0.4.4 && < 1++ if flag(text)+ build-depends: text >= 0.10 && < 2++ if flag(hashable)+ build-depends: hashable >= 1.1 && < 1.3++ if flag(hashable) && flag(unordered-containers)+ build-depends: unordered-containers >= 0.2 && < 0.3++ if flag(transformers)+ build-depends: transformers >= 0.2 && < 0.6+ , transformers-compat >= 0.5 && < 1
+ fixtures/semigroups.1.0.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/semigroups.1.2.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/semigroups.1.cabal view
@@ -0,0 +1,176 @@+name: semigroups+category: Algebra, Data, Data Structures, Math+version: 0.18.5+license: BSD3+cabal-version: >= 1.10+license-file: LICENSE+author: Edward A. Kmett+maintainer: Edward A. Kmett <ekmett@gmail.com>+stability: provisional+homepage: http://github.com/ekmett/semigroups/+bug-reports: http://github.com/ekmett/semigroups/issues+copyright: Copyright (C) 2011-2015 Edward A. Kmett+synopsis: Anything that associates+description:+ In mathematics, a semigroup is an algebraic structure consisting of a set together with an associative binary operation. A semigroup generalizes a monoid in that there might not exist an identity element. It also (originally) generalized a group (a monoid with all inverses) to a type where every element did not have to have an inverse, thus the name semigroup.+build-type: Simple+extra-source-files: .travis.yml README.markdown CHANGELOG.markdown+tested-with: GHC == 7.0.4+ , GHC == 7.2.2+ , GHC == 7.4.2+ , GHC == 7.6.3+ , GHC == 7.8.4+ , GHC == 7.10.3+ , GHC == 8.0.2+ , GHC == 8.2.2+ , GHC == 8.4.3+ , GHC == 8.6.1++source-repository head+ type: git+ location: git://github.com/ekmett/semigroups.git++flag hashable+ description:+ You can disable the use of the `hashable` package using `-f-hashable`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ .+ If disabled we will not supply instances of `Hashable`+ .+ Note: `-f-hashable` implies `-f-unordered-containers`, as we are necessarily not able to supply those instances as well.+ default: True+ manual: True++flag binary+ description:+ You can disable the use of the `binary` package using `-f-binary`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring+ description:+ You can disable the use of the `bytestring` package using `-f-bytestring`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring-builder+ description:+ Decides whether to use an older version of bytestring along with bytestring-builder or just a newer version of bytestring.+ .+ This flag normally toggles automatically but you can use `-fbytestring-builder` or `-f-bytestring-builder` to explicitly change it.+ default: False+ manual: False++flag containers+ description:+ You can disable the use of the `containers` package using `-f-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag deepseq+ description:+ You can disable the use of the `deepseq` package using `-f-deepseq`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag tagged+ description:+ You can disable the use of the `tagged` package using `-f-tagged`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag text+ description:+ You can disable the use of the `text` package using `-f-text`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag transformers+ description:+ You can disable the use of the `transformers` and `transformers-compat` packages using `-f-transformers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag unordered-containers+ description:+ You can disable the use of the `unordered-containers` package using `-f-unordered-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++library+ default-language: Haskell98+ hs-source-dirs: src+ ghc-options: -Wall++ build-depends: base >= 2 && < 5++ if impl(ghc >= 7.2)+ exposed-modules:+ Data.Semigroup.Generic++ -- legacy configuration+ if impl(ghc < 7.11.20151002)+ -- starting with GHC 8 these modules are provided by `base`+ hs-source-dirs: src-ghc7+ exposed-modules:+ Data.Semigroup+ Data.List.NonEmpty++ -- Not needed anymore since GHC 7.10+ if impl(ghc < 7.10)+ build-depends: nats >= 0.1 && < 2++ if impl(ghc >= 7.2 && < 7.5)+ build-depends: ghc-prim++ if flag(binary)+ build-depends: binary++ if flag(bytestring)+ if flag(bytestring-builder)+ build-depends: bytestring >= 0.9 && < 0.10.4,+ bytestring-builder >= 0.10.4 && < 1+ else+ build-depends: bytestring >= 0.10.4 && < 1++ if flag(containers)+ build-depends: containers >= 0.3 && < 0.8++ if flag(deepseq)+ build-depends: deepseq >= 1.1 && < 1.5++ if flag(tagged)+ build-depends: tagged >= 0.4.4 && < 1++ if flag(text)+ build-depends: text >= 0.10 && < 2++ if flag(hashable)+ build-depends: hashable >= 1.1 && < 1.3++ if flag(hashable) && flag(unordered-containers)+ build-depends: unordered-containers >= 0.2 && < 0.3++ if flag(transformers)+ build-depends: transformers >= 0.2 && < 0.6+ , transformers-compat >= 0.5 && < 1++ if impl(ghc==8.0.1)+ build-depends: base <0
+ fixtures/semigroups.2.0.diff view
@@ -0,0 +1,5 @@+OK+Normal+the library component's library dependency on 'containers'+- >=0.3 && <0.8++ >=0.3 && <0.7
+ fixtures/semigroups.2.1.diff view
@@ -0,0 +1,2 @@+ERROR+Cannot add or remove 'if' conditionals
+ fixtures/semigroups.2.cabal view
@@ -0,0 +1,173 @@+name: semigroups+category: Algebra, Data, Data Structures, Math+version: 0.18.5+license: BSD3+cabal-version: >= 1.10+license-file: LICENSE+author: Edward A. Kmett+maintainer: Edward A. Kmett <ekmett@gmail.com>+stability: provisional+homepage: http://github.com/ekmett/semigroups/+bug-reports: http://github.com/ekmett/semigroups/issues+copyright: Copyright (C) 2011-2015 Edward A. Kmett+synopsis: Anything that associates+description:+ In mathematics, a semigroup is an algebraic structure consisting of a set together with an associative binary operation. A semigroup generalizes a monoid in that there might not exist an identity element. It also (originally) generalized a group (a monoid with all inverses) to a type where every element did not have to have an inverse, thus the name semigroup.+build-type: Simple+extra-source-files: .travis.yml README.markdown CHANGELOG.markdown+tested-with: GHC == 7.0.4+ , GHC == 7.2.2+ , GHC == 7.4.2+ , GHC == 7.6.3+ , GHC == 7.8.4+ , GHC == 7.10.3+ , GHC == 8.0.2+ , GHC == 8.2.2+ , GHC == 8.4.3+ , GHC == 8.6.1++source-repository head+ type: git+ location: git://github.com/ekmett/semigroups.git++flag hashable+ description:+ You can disable the use of the `hashable` package using `-f-hashable`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ .+ If disabled we will not supply instances of `Hashable`+ .+ Note: `-f-hashable` implies `-f-unordered-containers`, as we are necessarily not able to supply those instances as well.+ default: True+ manual: True++flag binary+ description:+ You can disable the use of the `binary` package using `-f-binary`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring+ description:+ You can disable the use of the `bytestring` package using `-f-bytestring`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag bytestring-builder+ description:+ Decides whether to use an older version of bytestring along with bytestring-builder or just a newer version of bytestring.+ .+ This flag normally toggles automatically but you can use `-fbytestring-builder` or `-f-bytestring-builder` to explicitly change it.+ default: False+ manual: False++flag containers+ description:+ You can disable the use of the `containers` package using `-f-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag deepseq+ description:+ You can disable the use of the `deepseq` package using `-f-deepseq`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag tagged+ description:+ You can disable the use of the `tagged` package using `-f-tagged`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag text+ description:+ You can disable the use of the `text` package using `-f-text`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag transformers+ description:+ You can disable the use of the `transformers` and `transformers-compat` packages using `-f-transformers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++flag unordered-containers+ description:+ You can disable the use of the `unordered-containers` package using `-f-unordered-containers`.+ .+ Disabling this is an unsupported configuration, but it may be useful for accelerating builds in sandboxes for expert users.+ default: True+ manual: True++library+ default-language: Haskell98+ hs-source-dirs: src+ ghc-options: -Wall++ build-depends: base >= 2 && < 5++ if impl(ghc >= 7.2)+ exposed-modules:+ Data.Semigroup.Generic++ -- legacy configuration+ if impl(ghc < 7.11.20151002)+ -- starting with GHC 8 these modules are provided by `base`+ hs-source-dirs: src-ghc7+ exposed-modules:+ Data.Semigroup+ Data.List.NonEmpty++ -- Not needed anymore since GHC 7.10+ if impl(ghc < 7.10)+ build-depends: nats >= 0.1 && < 2++ if impl(ghc >= 7.2 && < 7.5)+ build-depends: ghc-prim++ if flag(binary)+ build-depends: binary++ if flag(bytestring)+ if flag(bytestring-builder)+ build-depends: bytestring >= 0.9 && < 0.10.4,+ bytestring-builder >= 0.10.4 && < 1+ else+ build-depends: bytestring >= 0.10.4 && < 1++ if flag(containers)+ build-depends: containers >= 0.3 && < 0.8++ if flag(deepseq)+ build-depends: deepseq >= 1.1 && < 1.5++ if flag(tagged)+ build-depends: tagged >= 0.4.4 && < 1++ if flag(text)+ build-depends: text >= 0.10 && < 2++ if flag(hashable)+ build-depends: hashable >= 1.1 && < 1.3++ if flag(hashable) && flag(unordered-containers)+ build-depends: unordered-containers >= 0.2 && < 0.3++ if flag(transformers)+ build-depends: transformers >= 0.2 && < 0.6+ , transformers-compat >= 0.5 && < 1
+ fixtures/tree-diff.0.1.diff view
@@ -0,0 +1,13 @@+OK+Trivial+tested-with+- ghc ==7.8.4: ghc ==7.10.3: ghc ==8.0.2: ghc ==8.2.2: ghc ==8.4.4: ghc ==8.6.2++ ghc ==7.8.4: ghc ==7.10.3: ghc ==8.0.2: ghc ==8.2.2: ghc ==8.4.4: ghc ==8.6.3+Normal+the library component's library dependency on 'ansi-terminal'+- >=0.6.3.1 && <0.9++ >=0.6.3.1 && <0.10+Normal+the test suite 'test' component's library dependency on 'tasty'+- >=0.11.2.5 && <1.2++ >=0.11.2.5 && <1.3
+ fixtures/tree-diff.0.cabal view
@@ -0,0 +1,129 @@+cabal-version: >=1.10+name: tree-diff+version: 0.0.2++synopsis: Diffing of (expression) trees.+category: Data, Testing+description:+ Common diff algorithm works on list structures:+ .+ @+ diff :: Eq a => [a] -> [a] -> [Edit a]+ @+ .+ This package works on trees.+ .+ @+ treeDiff :: Eq a => Tree a -> Tree a -> Edit (EditTree a)+ @+ .+ This package also provides a way to diff arbitrary ADTs,+ using @Generics@-derivable helpers.+ .+ This package differs from <http://hackage.haskell.org/package/gdiff gdiff>,+ in a two ways: @tree-diff@ doesn't have patch function,+ and the "edit-script" is a tree itself, which is useful for pretty-printing.+ .+ @+ >>> prettyEditExpr $ ediff (Foo 42 [True, False] "old") (Foo 42 [False, False, True] "new")+ Foo+ {fooBool = [-True, +False, False, +True],+ fooInt = 42,+ fooString = -"old" +"new"}+ @+homepage: https://github.com/phadej/tree-diff+bug-reports: https://github.com/phadej/tree-diff/issues+license: BSD3+license-file: LICENSE+author: Oleg Grenrus <oleg.grenrus@iki.fi>+maintainer: Oleg.Grenrus <oleg.grenrus@iki.fi>+copyright: (c) 2017-2018 Oleg Grenrus+build-type: Simple+extra-source-files: README.md ChangeLog.md+tested-with:+ GHC==7.8.4,+ GHC==7.10.3,+ GHC==8.0.2,+ GHC==8.2.2,+ GHC==8.4.4,+ GHc==8.6.2++extra-source-files:+ fixtures/exfoo.expr++source-repository head+ type: git+ location: https://github.com/phadej/tree-diff.git++library+ exposed-modules:+ Data.TreeDiff+ Data.TreeDiff.List+ Data.TreeDiff.Tree+ Data.TreeDiff.Expr+ Data.TreeDiff.Class+ Data.TreeDiff.Pretty+ Data.TreeDiff.Parser+ Data.TreeDiff.Golden+ Data.TreeDiff.QuickCheck+ build-depends:+ base >=4.7 && <4.13,+ aeson >=1.2.1.0 && <1.5,+ ansi-wl-pprint >=0.6.8.1 && <0.7,+ ansi-terminal >=0.6.3.1 && <0.9,+ base-compat >=0.9.3 && <0.11,+ bytestring >=0.10.4.0 && <0.11,+ containers >=0.5.5.1 && <0.7,+ generics-sop >=0.3.1.0 && <0.5,+ hashable >=1.2.6.1 && <1.3,+ MemoTrie >=0.6.8 && <0.7,+ parsec >=3.1.11 && <3.2,+ parsers >=0.12.7 && <0.13,+ pretty >=1.1.1.1 && <1.2,+ QuickCheck >=2.10.0.1 && <2.13,+ scientific >=0.3.5.2 && <0.4,+ tagged >=0.8.5 && <0.9,+ text >=1.2.2.2 && <1.3,+ time >=1.4.2 && <1.9,+ unordered-containers >=0.2.8.0 && <0.3,+ uuid-types >=1.0.3 && <1.1,+ vector >=0.12 && <0.13++ if !impl(ghc >= 8.0)+ build-depends:+ semigroups >=0.18.3 && <0.19++ if !impl(ghc >= 7.10)+ build-depends:+ void >=0.7.2 && <0.8,+ nats >=1.1.1 && <1.2,+ transformers >=0.3.0.0 && <0.6++ other-extensions:+ ConstraintKinds+ CPP+ DefaultSignatures+ FlexibleContexts+ GADTs+ RankNTypes+ ScopedTypeVariables+ hs-source-dirs: src+ default-language: Haskell2010++test-suite test+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Tests.hs+ hs-source-dirs: tests+ ghc-options: -Wall -threaded+ build-depends:+ base, tree-diff,+ base-compat,+ QuickCheck,+ ansi-terminal,+ ansi-wl-pprint,+ parsec,+ trifecta >=1.7.1.1 && <2.1,+ tasty >=0.11.2.5 && <1.2,+ tasty-golden >=2.3.1.1 && <2.4,+ tasty-quickcheck >=0.9.1 && <0.11
+ fixtures/tree-diff.1.0.diff view
@@ -0,0 +1,13 @@+OK+Trivial+tested-with+- ghc ==7.8.4: ghc ==7.10.3: ghc ==8.0.2: ghc ==8.2.2: ghc ==8.4.4: ghc ==8.6.3++ ghc ==7.8.4: ghc ==7.10.3: ghc ==8.0.2: ghc ==8.2.2: ghc ==8.4.4: ghc ==8.6.2+Normal+the library component's library dependency on 'ansi-terminal'+- >=0.6.3.1 && <0.10++ >=0.6.3.1 && <0.9+Normal+the test suite 'test' component's library dependency on 'tasty'+- >=0.11.2.5 && <1.3++ >=0.11.2.5 && <1.2
+ fixtures/tree-diff.1.cabal view
@@ -0,0 +1,130 @@+cabal-version: >=1.10+name: tree-diff+version: 0.0.2+x-revision: 1++synopsis: Diffing of (expression) trees.+category: Data, Testing+description:+ Common diff algorithm works on list structures:+ .+ @+ diff :: Eq a => [a] -> [a] -> [Edit a]+ @+ .+ This package works on trees.+ .+ @+ treeDiff :: Eq a => Tree a -> Tree a -> Edit (EditTree a)+ @+ .+ This package also provides a way to diff arbitrary ADTs,+ using @Generics@-derivable helpers.+ .+ This package differs from <http://hackage.haskell.org/package/gdiff gdiff>,+ in a two ways: @tree-diff@ doesn't have patch function,+ and the "edit-script" is a tree itself, which is useful for pretty-printing.+ .+ @+ >>> prettyEditExpr $ ediff (Foo 42 [True, False] "old") (Foo 42 [False, False, True] "new")+ Foo+ {fooBool = [-True, +False, False, +True],+ fooInt = 42,+ fooString = -"old" +"new"}+ @+homepage: https://github.com/phadej/tree-diff+bug-reports: https://github.com/phadej/tree-diff/issues+license: BSD3+license-file: LICENSE+author: Oleg Grenrus <oleg.grenrus@iki.fi>+maintainer: Oleg.Grenrus <oleg.grenrus@iki.fi>+copyright: (c) 2017-2018 Oleg Grenrus+build-type: Simple+extra-source-files: README.md ChangeLog.md+tested-with:+ GHC==7.8.4,+ GHC==7.10.3,+ GHC==8.0.2,+ GHC==8.2.2,+ GHC==8.4.4,+ GHc==8.6.3++extra-source-files:+ fixtures/exfoo.expr++source-repository head+ type: git+ location: https://github.com/phadej/tree-diff.git++library+ exposed-modules:+ Data.TreeDiff+ Data.TreeDiff.List+ Data.TreeDiff.Tree+ Data.TreeDiff.Expr+ Data.TreeDiff.Class+ Data.TreeDiff.Pretty+ Data.TreeDiff.Parser+ Data.TreeDiff.Golden+ Data.TreeDiff.QuickCheck+ build-depends:+ base >=4.7 && <4.13,+ aeson >=1.2.1.0 && <1.5,+ ansi-wl-pprint >=0.6.8.1 && <0.7,+ ansi-terminal >=0.6.3.1 && <0.10,+ base-compat >=0.9.3 && <0.11,+ bytestring >=0.10.4.0 && <0.11,+ containers >=0.5.5.1 && <0.7,+ generics-sop >=0.3.1.0 && <0.5,+ hashable >=1.2.6.1 && <1.3,+ MemoTrie >=0.6.8 && <0.7,+ parsec >=3.1.11 && <3.2,+ parsers >=0.12.7 && <0.13,+ pretty >=1.1.1.1 && <1.2,+ QuickCheck >=2.10.0.1 && <2.13,+ scientific >=0.3.5.2 && <0.4,+ tagged >=0.8.5 && <0.9,+ text >=1.2.2.2 && <1.3,+ time >=1.4.2 && <1.9,+ unordered-containers >=0.2.8.0 && <0.3,+ uuid-types >=1.0.3 && <1.1,+ vector >=0.12 && <0.13++ if !impl(ghc >= 8.0)+ build-depends:+ semigroups >=0.18.3 && <0.19++ if !impl(ghc >= 7.10)+ build-depends:+ void >=0.7.2 && <0.8,+ nats >=1.1.1 && <1.2,+ transformers >=0.3.0.0 && <0.6++ other-extensions:+ ConstraintKinds+ CPP+ DefaultSignatures+ FlexibleContexts+ GADTs+ RankNTypes+ ScopedTypeVariables+ hs-source-dirs: src+ default-language: Haskell2010++test-suite test+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: Tests.hs+ hs-source-dirs: tests+ ghc-options: -Wall -threaded+ build-depends:+ base, tree-diff,+ base-compat,+ QuickCheck,+ ansi-terminal,+ ansi-wl-pprint,+ parsec,+ trifecta >=1.7.1.1 && <2.1,+ tasty >=0.11.2.5 && <1.3,+ tasty-golden >=2.3.1.1 && <2.4,+ tasty-quickcheck >=0.9.1 && <0.11
+ hackage-cli.cabal view
@@ -0,0 +1,122 @@+cabal-version: 2.4+name: hackage-cli+version: 0.0.3.6++synopsis: CLI tool for Hackage+description:+ With @hackage-cli@ you can manage @.cabal@ files,+ e.g. (bulk-)upload revised variants.+homepage: https://github.com/hackage-trustees/hackage-cli+bug-reports: https://github.com/hackage-trustees/hackage-cli/issues+license: GPL-3.0-or-later+license-file: LICENSE+author: Herbert Valerio Riedel+maintainer: Andreas Abel+category: Development+build-type: Simple++tested-with:+ -- Keep in descending order.+ GHC == 9.2.2+ GHC == 9.0.2+ GHC == 8.10.7+ GHC == 8.8.4+ GHC == 8.6.5+ GHC == 8.4.4+ GHC == 8.2.2++extra-source-files:+ CHANGELOG.md+ README.md+ fixtures/*.diff+ fixtures/*.cabal++source-repository head+ Type: git+ Location: https://github.com/hackage-trustees/hackage-cli.git++library cabal-revisions+ X-SPDX-License-Identifier: BSD-3-Clause+ default-language: Haskell2010+ hs-source-dirs: lib+ ghc-options: -Wall -Wcompat++ build-depends:+ , base >= 4.10.0.0 && < 4.17+ , bytestring >= 0.10.4.0 && < 0.12+ , Cabal >= 3.4 && < 3.7+ , containers >= 0.5.0.0 && < 0.7+ , mtl >= 2.2.2 && < 2.4+ , pretty ^>= 1.1.2++ exposed-modules:+ Distribution.Server.Util.CabalRevisions++test-suite cabal-revisions-tests+ default-language: Haskell2010+ hs-source-dirs: tests+ ghc-options: -Wall -Wcompat+ type: exitcode-stdio-1.0+ main-is: Tests.hs+ build-depends:+ , base+ , bytestring+ , Cabal+ , cabal-revisions+ , tasty >= 1.0 && < 1.5+ -- tasty-1.0 for stack-8.2.2.yaml+ , tasty-golden ^>= 2.3.2+ , filepath ^>= 1.4.0.0++executable hackage-cli+ default-language: Haskell2010+ other-extensions:+ CPP+ LambdaCase+ OverloadedStrings+ RecordWildCards+ TemplateHaskell++ hs-source-dirs: src+ main-is: Main.hs++ other-modules:+ IndexShaSum+ CabalEdit+ Paths_hackage_cli++ autogen-modules:+ Paths_hackage_cli++ build-depends:+ , base+ , bytestring+ , Cabal+ , cabal-revisions+ , containers+ , mtl++ build-depends:+ , aeson >= 1.2.4.0 && < 2.1+ -- aeson-1.2.4.0 for stack-8.2.2.yaml+ , deepseq ^>= 1.4.0.0+ , directory ^>= 1.2.0.1 || ^>= 1.3.0.0+ , filepath ^>= 1.4.0.0+ , http-io-streams ^>= 0.1.0.0+ , io-streams ^>= 1.5.0.1+ , microlens >= 0.4.8.3 && < 4.13+ , microlens-mtl >= 0.1.11.1 && < 0.3+ , microlens-th >= 0.4.1.3 && < 0.5+ , netrc ^>= 0.2.0.0+ , optparse-applicative >= 0.14 && < 0.18+ , process-extras ^>= 0.7.4+ , semigroups >= 0.18.3 && < 0.21+ , stringsearch ^>= 0.3.6+ , tagsoup ^>= 0.14+ , tar ^>= 0.5+ , text >= 1.2 && < 2.1+ , time >= 1.5.0.1 && < 1.13+ , unordered-containers ^>= 0.2.7+ , zlib ^>= 0.6.1++ ghc-options: -Wall -Wcompat -threaded
+ lib/Distribution/Server/Util/CabalRevisions.hs view
@@ -0,0 +1,771 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeFamilies #-}++-- |+-- Module : Distribution.Server.Util.CabalRevisions+-- Copyright : Duncan Coutts et al.+-- SPDX-License-Identifier: BSD-3-Clause+--+-- Maintainer : libraries@haskell.org+-- Stability : provisional+-- Portability : portable+--+-- Validation and helpers for Cabal revision handling+module Distribution.Server.Util.CabalRevisions+ ( diffCabalRevisions+ , diffCabalRevisions'+ , Change(..)+ , insertRevisionField+ ) where++-- NB: This module avoids to import any hackage-server modules+import Distribution.CabalSpecVersion (CabalSpecVersion(..), cabalSpecLatest, showCabalSpecVersion)+import Distribution.Types.Dependency+import Distribution.Types.ExeDependency+import Distribution.Types.PkgconfigDependency+import Distribution.Types.PkgconfigVersionRange+import Distribution.Types.LegacyExeDependency+import Distribution.Types.UnqualComponentName+import Distribution.Types.CondTree+import Distribution.Types.ForeignLib+import Distribution.Package+import Distribution.Pretty (Pretty (..), prettyShow)+import Distribution.Version+import Distribution.Compiler (CompilerFlavor)+import Distribution.FieldGrammar (prettyFieldGrammar)+import Distribution.Fields.Pretty (PrettyField (..), showFields)+import Distribution.PackageDescription+import Distribution.PackageDescription.Parsec (parseGenericPackageDescription, runParseResult)+import Distribution.PackageDescription.FieldGrammar (sourceRepoFieldGrammar)+import Distribution.PackageDescription.Check+import Distribution.Parsec (showPWarning, showPError, PWarning (..))+import Distribution.Utils.ShortText+import Text.PrettyPrint as Doc+ ((<+>), colon, text, Doc, hsep, punctuate)++import Control.Applicative+import Control.Monad+import Control.Monad.Except (ExceptT, runExceptT, throwError)+import Control.Monad.Writer (MonadWriter(..), Writer, runWriter)+import Data.Foldable (for_)+import Data.List+ ((\\), deleteBy, intercalate)+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy.Char8 as LBS8+import qualified Data.Char as Char+import qualified Data.Semigroup as S+import qualified Data.Monoid as M+import qualified Data.Set as Set+import qualified Data.Map.Strict as Map+import Data.Proxy (Proxy(Proxy))++import qualified Control.Monad.Fail as Fail++-- | Entry point to cabal revision validator+--+-- This takes an original and a revised @.cabal@ decoded as Unicode+-- 'String' and performs validations. Returns either a validation+-- error or a list of detected changes.+diffCabalRevisions :: BS.ByteString -> BS.ByteString -> Either String [Change]+diffCabalRevisions = diffCabalRevisions' True++-- | Like 'diffCabalRevisions' but only optionally check @x-revision@ field modifications.+diffCabalRevisions'+ :: Bool -- ^ check @x-revision@+ -> BS.ByteString -- ^ old revision+ -> BS.ByteString -- ^ new revision+ -> Either String [Change]+diffCabalRevisions' checkXRevision oldVersion newRevision = runCheck $+ checkCabalFileRevision checkXRevision oldVersion newRevision++newtype CheckM a = CheckM { unCheckM :: ExceptT String (Writer [Change]) a }+ deriving (Functor, Applicative)++runCheck :: CheckM () -> Either String [Change]+runCheck c = case runWriter . runExceptT . unCheckM $ c of+ (Left err, _ ) -> Left err+ (Right (), changes)+ | foldMap changeSeverity changes /= Trivial -> Right changes+ | otherwise ->+ Left "Only trivial changes, don't bother making this revision."++changeSeverity :: Change -> Severity+changeSeverity (Change s _ _ _) = s++instance Monad CheckM where+ return = Control.Applicative.pure+ CheckM m >>= f = CheckM (m >>= unCheckM . f)++#if !MIN_VERSION_base(4,13,0)+ fail = Fail.fail+#endif++instance Fail.MonadFail CheckM where+ fail = CheckM . throwError++-- | If we have only 'Trivial' changes, then there is no point to make+-- a revision. In other words for changes to be accepted, there should+-- be at least one 'Normal' change.+data Severity+ = Normal+ | Trivial+ deriving (Eq, Ord, Show, Enum, Bounded)++instance S.Semigroup Severity where+ Normal <> _ = Normal+ Trivial <> x = x++-- | "Max" monoid.+instance M.Monoid Severity where+ mempty = Trivial+ mappend = (S.<>)++data Change = Change Severity String String String -- severity, what, from, to+ deriving Show++++logChange :: Change -> CheckM ()+logChange change = CheckM (tell [change])++type Check a = a -> a -> CheckM ()++checkCabalFileRevision :: Bool -> Check BS.ByteString+checkCabalFileRevision checkXRevision old new = do+ (pkg, warns) <- parseCabalFile old+ (pkg', warns') <- parseCabalFile new++ let pkgid = packageId pkg+ filename = prettyShow pkgid ++ ".cabal"++ checkGenericPackageDescription checkXRevision pkg pkg'+ checkParserWarnings filename warns warns'+ checkPackageChecks pkg pkg'++ where+ parseCabalFile fileContent =+ case runParseResult $ parseGenericPackageDescription fileContent of+ (warnings, Right pkg) -> return (pkg, warnings)+ (_warnings, Left (_mver, errs)) -> do+ for_ errs $ \err -> fail (showPError "-" err)+ fail "no better error"++ -- new PWarning isn't Eq+ differenceBy :: (a -> a -> Bool) -> [a] -> [a] -> [a]+ differenceBy eq = foldl (flip $ deleteBy eq)++ -- things can move, pos can change+ eqPWarning :: PWarning -> PWarning -> Bool+ eqPWarning (PWarning t _pos s) (PWarning t' _pos' s') =+ t == t' && s == s'++ checkParserWarnings :: FilePath -> Check [PWarning]+ checkParserWarnings filename warns warns' =+ case differenceBy eqPWarning warns' warns of+ [] -> return ()+ newwarns -> fail $ "New parse warning: "+ ++ unlines (map (showPWarning filename) newwarns)++ checkPackageChecks pkg pkg' =+ let checks = checkPackage pkg Nothing+ checks' = checkPackage pkg' Nothing+ in case checks' \\ checks of+ [] -> return ()+ newchecks -> fail $ unlines (map explanation newchecks)++checkGenericPackageDescription :: Bool -> Check GenericPackageDescription+checkGenericPackageDescription checkXRevision+ (GenericPackageDescription descrA _versionA flagsA libsA sublibsA flibsA exesA testsA benchsA)+ (GenericPackageDescription descrB _versionB flagsB libsB sublibsB flibsB exesB testsB benchsB) = do++ checkPackageDescriptions checkXRevision descrA descrB++ checkList "Cannot add or remove flags" checkFlag flagsA flagsB++ checkMaybe "Cannot add or remove library sections"+ (checkCondTree checkLibrary)+ (withComponentName' (CLibName LMainLibName) <$> libsA)+ (withComponentName' (CLibName LMainLibName) <$> libsB)++ checkListAssoc "Cannot add or remove sub-library sections"+ (checkCondTree checkLibrary)+ (withComponentName (CLibName . LSubLibName) <$> sublibsA)+ (withComponentName (CLibName . LSubLibName) <$> sublibsB)++ checkListAssoc "Cannot add or remove foreign-library sections"+ (checkCondTree checkForeignLib)+ (withComponentName CFLibName <$> flibsA)+ (withComponentName CFLibName <$> flibsB)++ checkListAssoc "Cannot add or remove executable sections"+ (checkCondTree checkExecutable)+ (withComponentName CExeName <$> exesA)+ (withComponentName CExeName <$> exesB)++ checkListAssoc "Cannot add or remove test-suite sections"+ (checkCondTree checkTestSuite)+ (withComponentName CTestName <$> testsA)+ (withComponentName CTestName <$> testsB)++ checkListAssoc "Cannot add or remove benchmark sections"+ (checkCondTree checkBenchmark)+ (withComponentName CBenchName <$> benchsA)+ (withComponentName CBenchName <$> benchsB)+ where+ withComponentName f (name, condTree) = (name, (f name, condTree))+ withComponentName' f condTree = (f, condTree)+++checkFlag :: Check PackageFlag+checkFlag flagOld flagNew = do+ -- This check is applied via 'checkList' and for simplicity we+ -- disallow renaming/reordering flags (even though reordering+ -- would be fine semantically)+ checkSame "Cannot change ordering of flags"+ (flagName flagOld) (flagName flagNew)++ -- Automatic flags' defaults may be changed as they don't make new+ -- configurations reachable by the solver that weren't before+ --+ -- Moreover, automatic flags may be converted into manual flags+ -- but not the other way round.+ --+ -- NB: We always allow to change the flag description as it has+ -- purely informational value+ when (flagManual flagOld) $ do+ checkSame "Cannot change the default of a manual flag"+ (flagDefault flagOld) (flagDefault flagNew)++ checkSame "Cannot change a manual flag into an automatic flag"+ (flagManual flagOld) (flagManual flagNew)++ let fname = unFlagName (flagName flagOld)++ changesOk ("type of flag '" ++ fname ++ "'")+ (\b -> if b then "manual" else "automatic")+ (flagManual flagOld) (flagManual flagNew)++ changesOk ("default of flag '" ++ fname ++ "'") prettyShow+ (flagDefault flagOld) (flagDefault flagNew)++ changesOk ("description of flag '" ++ fname ++ "'") id+ (flagDescription flagOld) (flagDescription flagNew)++checkPackageDescriptions :: Bool -> Check PackageDescription+checkPackageDescriptions checkXRevision+ pdA@(PackageDescription+ { specVersion = _specVersionA+ , package = packageIdA+ , licenseRaw = licenseRawA+ , licenseFiles = licenseFilesA+ , copyright = copyrightA+ , maintainer = maintainerA+ , author = authorA+ , stability = stabilityA+ , testedWith = testedWithA+ , homepage = homepageA+ , pkgUrl = pkgUrlA+ , bugReports = bugReportsA+ , sourceRepos = sourceReposA+ , synopsis = synopsisA+ , description = descriptionA+ , category = categoryA+ , customFieldsPD = customFieldsPDA+ , buildTypeRaw = buildTypeRawA+ , setupBuildInfo = setupBuildInfoA+ , library = _libraryA+ , subLibraries = _subLibrariesA+ , executables = _executablesA+ , foreignLibs = _foreignLibsA+ , testSuites = _testSuitesA+ , benchmarks = _benchmarksA+ , dataFiles = dataFilesA+ , dataDir = dataDirA+ , extraSrcFiles = extraSrcFilesA+ , extraTmpFiles = extraTmpFilesA+ , extraDocFiles = extraDocFilesA+ })+ pdB@(PackageDescription+ { specVersion = _specVersionB+ , package = packageIdB+ , licenseRaw = licenseRawB+ , licenseFiles = licenseFilesB+ , copyright = copyrightB+ , maintainer = maintainerB+ , author = authorB+ , stability = stabilityB+ , testedWith = testedWithB+ , homepage = homepageB+ , pkgUrl = pkgUrlB+ , bugReports = bugReportsB+ , sourceRepos = sourceReposB+ , synopsis = synopsisB+ , description = descriptionB+ , category = categoryB+ , customFieldsPD = customFieldsPDB+ , buildTypeRaw = buildTypeRawB+ , setupBuildInfo = setupBuildInfoB+ , library = _libraryB+ , subLibraries = _subLibrariesB+ , executables = _executablesB+ , foreignLibs = _foreignLibsB+ , testSuites = _testSuitesB+ , benchmarks = _benchmarksB+ , dataFiles = dataFilesB+ , dataDir = dataDirB+ , extraSrcFiles = extraSrcFilesB+ , extraTmpFiles = extraTmpFilesB+ , extraDocFiles = extraDocFilesB+ })+ = do+ checkSame "Don't be silly! You can't change the package name!"+ (packageName packageIdA) (packageName packageIdB)+ checkSame "You can't change the package version!"+ (packageVersion packageIdA) (packageVersion packageIdB)+ checkSame "Cannot change the license"+ (licenseRawA, licenseFilesA) (licenseRawB, licenseFilesB)+ changesOk "copyright" fromShortText copyrightA copyrightB+ changesOk "maintainer" fromShortText maintainerA maintainerB+ changesOk "author" fromShortText authorA authorB+ checkSame "The stability field is unused, don't bother changing it."+ stabilityA stabilityB+ changesOk' Trivial "tested-with" (show . ppTestedWith) testedWithA testedWithB+ changesOk "homepage" fromShortText homepageA homepageB+ checkSame "The package-url field is unused, don't bother changing it."+ pkgUrlA pkgUrlB+ changesOk "bug-reports" fromShortText bugReportsA bugReportsB+ changesOkList changesOk "source-repository" (showFields (const []) . (:[]) . ppSourceRepo)+ sourceReposA sourceReposB+ changesOk "synopsis" fromShortText synopsisA synopsisB+ changesOk "description" fromShortText descriptionA descriptionB+ changesOk "category" fromShortText categoryA categoryB+ checkSame "Cannot change the build-type"+ buildTypeRawA buildTypeRawB+ checkSame "Cannot change the data files"+ (dataFilesA, dataDirA) (dataFilesB, dataDirB)+ checkSame "Changing extra-tmp-files is a bit pointless at this stage"+ extraTmpFilesA extraTmpFilesB+ checkSame "Changing extra-source-files would not make sense!"+ extraSrcFilesA extraSrcFilesB+ checkSame "You can't change the extra-doc-files."+ extraDocFilesA extraDocFilesB++ checkSame "Cannot change custom/extension fields"+ (filter (\(f,_) -> not (f `elem` ["x-revision","x-curation"])) customFieldsPDA)+ (filter (\(f,_) -> not (f `elem` ["x-revision","x-curation"])) customFieldsPDB)++ checkSpecVersionRaw pdA pdB+ checkSetupBuildInfo setupBuildInfoA setupBuildInfoB++ when checkXRevision $ checkRevision customFieldsPDA customFieldsPDB+ checkCuration customFieldsPDA customFieldsPDB++checkSpecVersionRaw :: Check PackageDescription+checkSpecVersionRaw pdA pdB+ | range110To120 specVersionA+ , range110To120 specVersionB+ = changesOk "cabal-version" showCabalSpecVersion specVersionA specVersionB++ | otherwise+ = checkSame "Cannot change the Cabal spec version"+ specVersionA specVersionB+ where+ specVersionA = specVersion pdA+ specVersionB = specVersion pdB++ -- nothing interesting changed within the Cabal >=1.10 && <1.21 range+ -- therefore we allow to change the spec version within this interval+ range110To120 v = CabalSpecV1_10 >= v && v <= CabalSpecV1_20++checkRevision :: Check [(String, String)]+checkRevision customFieldsA customFieldsB =+ checkSame ("The new x-revision must be " ++ show expectedRevision)+ newRevision expectedRevision+ where+ oldRevision = getRevision customFieldsA+ newRevision = getRevision customFieldsB+ expectedRevision = oldRevision + 1++ getRevision customFields =+ case lookup "x-revision" customFields of+ Just s | [(n,"")] <- reads s -> n :: Int+ _ -> 0++checkCuration :: Check [(String, String)]+checkCuration customFieldsA customFieldsB =+ checkNotPresent "Revised metadata must not contain an x-curation field as revisions necessarily imply curation, and revising an uncurated package adopts it into the curated layer." oldCuration newCuration+ where+ oldCuration = lookup "x-curation" customFieldsA+ newCuration = lookup "x-curation" customFieldsB++++checkCondTree :: (ComponentName -> Check a) -> Check (ComponentName, CondTree ConfVar [Dependency] a)+checkCondTree checkElem (componentName, condNodeA)+ (_ , condNodeB) =+ checkCondNode condNodeA condNodeB+ where+ checkCondNode (CondNode dataA constraintsA componentsA)+ (CondNode dataB constraintsB componentsB) = do+ checkDependencies componentName constraintsA constraintsB+ checkList "Cannot add or remove 'if' conditionals"+ checkComponent componentsA componentsB+ checkElem componentName dataA dataB++ checkComponent (CondBranch condA ifPartA thenPartA)+ (CondBranch condB ifPartB thenPartB) = do+ checkSame "Cannot change the 'if' condition expressions"+ condA condB+ checkCondNode ifPartA ifPartB+ checkMaybe "Cannot add or remove the 'else' part in conditionals"+ checkCondNode thenPartA thenPartB++checkDependencies :: forall d vr. (Pretty d, IsDependency vr d) => ComponentName -> Check [d]+checkDependencies componentName ds1 ds2 = do+ forM_ removed $ \dep -> do+ fail (unwords [ "Cannot remove existing", depKind, "on"+ , depKeyShow dproxy (depKey dep), "in", cnameStr, " component"])++ forM_ added $ \dep ->+ if depInAddWhitelist dep+ then logChange (Change Normal (unwords ["added the", cnameStr, "component's"+ , depKind, "on"]) "" (prettyShow dep))+ else fail (unwords [ "Cannot add new", depKind, "on"+ , depKeyShow dproxy (depKey dep)+ , "in", cnameStr, "component"])++ forM_ changed $ \(depk, (verA, verB)) -> do+ changesOk (unwords ["the", cnameStr, "component's", depKind, "on"+ , depKeyShow dproxy depk])+ prettyShow verA verB+ where+ (removed, changed, added) = computeCanonDepChange ds1 ds2++ dproxy :: Proxy d+ dproxy = Proxy++ cnameStr = showComponentName componentName++ depKind = depTypeName dproxy ++ " dependency"++class (Ord (DepKey d), Pretty vr, Eq vr) => IsDependency vr d | d -> vr where+ type DepKey d++ depTypeName :: Proxy d -> String+ depKey :: d -> DepKey d+ depKeyShow :: Proxy d -> DepKey d -> String+ depVerRg :: d -> vr+ reconstructDep :: DepKey d -> vr -> d++ depInAddWhitelist :: d -> Bool+ depInAddWhitelist _ = False++ intersectVr :: Proxy d -> vr -> vr -> vr++instance IsDependency VersionRange Dependency where+ type DepKey Dependency = PackageName++ depTypeName Proxy = "library"+ depKey (Dependency pkgname _ _) = pkgname+ depKeyShow Proxy = prettyShow''+ depVerRg (Dependency _ vr _) = vr+ reconstructDep = \n vr -> Dependency n vr mainLibSet++ depInAddWhitelist (Dependency pn _ _) = pn `elem`+ -- Special case: there are some pretty weird broken packages out there, see+ -- https://github.com/haskell/hackage-server/issues/303+ -- which need us to add a new dep on `base`+ [ mkPackageName "base"++ -- See also https://github.com/haskell/hackage-server/issues/472+ --+ -- this is mostly to allow to add dependencies on `base-orphans == 0`+ -- as otherwise we have no way to express when a package is+ -- incompatible with the recently introduced `base-orphans` package+ -- which started adopting orphan instances; in the long-term we need a+ -- more general approach to this, as otherwise we'll end up adding+ -- ad-hoc exceptions like this one. See e.g.+ -- https://github.com/haskell/cabal/issues/3061+ --+ , mkPackageName "base-orphans"+ ]++ intersectVr _ = intersectVersionRanges+++instance IsDependency VersionRange ExeDependency where+ type DepKey ExeDependency = (PackageName,UnqualComponentName)++ depTypeName Proxy = "tool"+ depKey (ExeDependency pkgname cn _) = (pkgname,cn)+ depKeyShow Proxy (pkgname,cn) = concat ["'", prettyShow pkgname, ":", prettyShow cn, "'"]+ depVerRg (ExeDependency _ _ vr) = vr+ reconstructDep (pkgname,cn) = ExeDependency pkgname cn++ intersectVr _ = intersectVersionRanges++instance IsDependency VersionRange LegacyExeDependency where+ type DepKey LegacyExeDependency = String++ depTypeName Proxy = "legacy-tool"+ depKey (LegacyExeDependency tname _) = tname+ depKeyShow Proxy tname = "'" ++ tname ++ "'"+ depVerRg (LegacyExeDependency _ vr) = vr+ reconstructDep = LegacyExeDependency++ intersectVr _ = intersectVersionRanges++ depInAddWhitelist (LegacyExeDependency pn _) = pn `elem`+ -- list of trusted tools cabal supports w/o explicit build-tools+ -- c.f. Distribution.Simple.BuildToolDepends.desugarBuildTool+ -- and 'knownSuffixHandlers' in "Distribution.Client.Init.Heuristics"+ [ "alex"+ , "c2hs"+ , "cpphs"+ , "greencard"+ , "happy"+ , "hsc2hs"+ ]++instance IsDependency PkgconfigVersionRange PkgconfigDependency where+ type DepKey PkgconfigDependency = PkgconfigName++ depTypeName Proxy = "pkg-config"+ depKey (PkgconfigDependency pkgname _) = pkgname+ depKeyShow Proxy = prettyShow''+ depVerRg (PkgconfigDependency _ vr) = vr+ reconstructDep = PkgconfigDependency++ intersectVr _ = PcIntersectVersionRanges+++-- The result tuple represents the 3 canonicalised dependency+-- (removed deps (old ranges), retained deps (old & new ranges), added deps (new ranges))+-- or expressed as set-operations: (A \ B, (A ∩ B), B \ A)+computeCanonDepChange :: forall vr d. IsDependency vr d => [d] -> [d] -> ([d],[(DepKey d,(vr,vr))],[d])+computeCanonDepChange depsA depsB+ = ( mapToDeps (a `Map.difference` b)+ , Map.toList $ Map.intersectionWith (,) a b+ , mapToDeps (b `Map.difference` a)+ )+ where+ a = depsToMapWithCanonVerRange depsA+ b = depsToMapWithCanonVerRange depsB++ depsToMapWithCanonVerRange+ = Map.fromListWith (flip $ intersectVr (Proxy :: Proxy d)) .+ map (\d -> (depKey d, depVerRg d))++ mapToDeps+ = map (\(pkgname, verrange) -> reconstructDep pkgname verrange) . Map.toList+++checkSetupBuildInfo :: Check (Maybe SetupBuildInfo)+checkSetupBuildInfo Nothing Nothing = return ()+checkSetupBuildInfo (Just _) Nothing =+ fail "Cannot remove a 'custom-setup' section"++checkSetupBuildInfo Nothing (Just (SetupBuildInfo setupDependsA _internalA)) =+ logChange $ Change Normal+ ("added a 'custom-setup' section with 'setup-depends'")+ "[implicit]" (intercalate ", " (map prettyShow setupDependsA))++checkSetupBuildInfo (Just (SetupBuildInfo setupDependsA _internalA))+ (Just (SetupBuildInfo setupDependsB _internalB)) = do+ forM_ removed $ \dep ->+ logChange $ Change Normal ("removed 'custom-setup' dependency on") (prettyShow dep) ""+ forM_ added $ \dep ->+ logChange $ Change Normal ("added 'custom-setup' dependency on") "" (prettyShow dep)+ forM_ changed $ \(pkgn, (verA, verB)) ->+ changesOk ("the 'custom-setup' dependency on " ++ prettyShow'' pkgn)+ prettyShow verA verB+ where+ (removed, changed, added) =+ computeCanonDepChange setupDependsA setupDependsB++checkLibrary :: ComponentName -> Check Library+checkLibrary componentName+ (Library modulesA reexportedA requiredSigsA exposedSigsA+ exposedA visibilityA buildInfoA)+ (Library modulesB reexportedB requiredSigsB exposedSigsB+ exposedB visibilityB buildInfoB) = do+ checkSame "Cannot change the exposed modules" modulesA modulesB+ checkSame "Cannot change the re-exported modules" reexportedA reexportedB+ checkSame "Cannot change the required signatures" requiredSigsA requiredSigsB+ checkSame "Cannot change the exposed signatures" exposedSigsA exposedSigsB+ checkSame "Cannot change the package exposed status" exposedA exposedB+ checkSame "Cannot change the package visibility" visibilityA visibilityB+ checkBuildInfo componentName buildInfoA buildInfoB++checkForeignLib :: ComponentName -> Check ForeignLib+checkForeignLib componentName+ (ForeignLib nameA typeA optionsA buildInfoA verA verLinuxA modDefA)+ (ForeignLib nameB typeB optionsB buildInfoB verB verLinuxB modDefB) = do+ checkSame "Cannot change the foreign library name" nameA nameB+ checkSame "Cannot change the foreign library type" typeA typeB+ checkSame "Cannot change the foreign library options" optionsA optionsB+ checkSame "Cannot change the foreign library version" verA verB+ checkSame "Cannot change the foreign library version for Linux" verLinuxA verLinuxB+ checkSame "Cannot change the module definition files" modDefA modDefB+ checkBuildInfo componentName buildInfoA buildInfoB++checkExecutable :: ComponentName -> Check Executable+checkExecutable componentName+ (Executable _nameA pathA scopeA buildInfoA)+ (Executable _nameB pathB scopeB buildInfoB) = do+ checkSame "Cannot change build information" pathA pathB+ checkSame "Cannot change executable scope" scopeA scopeB+ checkBuildInfo componentName buildInfoA buildInfoB++checkTestSuite :: ComponentName -> Check TestSuite+checkTestSuite componentName+ (TestSuite _nameA interfaceA buildInfoA)+ (TestSuite _nameB interfaceB buildInfoB) = do+ checkSame "Cannot change test-suite type" interfaceA interfaceB+ checkBuildInfo componentName buildInfoA buildInfoB++checkBenchmark :: ComponentName -> Check Benchmark+checkBenchmark componentName+ (Benchmark _nameA interfaceA buildInfoA)+ (Benchmark _nameB interfaceB buildInfoB) = do+ checkSame "Cannot change benchmark type" interfaceA interfaceB+ checkBuildInfo componentName buildInfoA buildInfoB++checkBuildInfo :: ComponentName -> Check BuildInfo+checkBuildInfo componentName biA biB = do+ -- @other-extension@+ changesOkSet ("'other-extensions' in " ++ showComponentName componentName ++ " component")+ prettyShow+ (Set.fromList $ otherExtensions biA) (Set.fromList $ otherExtensions biB)++ -- @buildable@+ changesOk ("'buildable' in " ++ showComponentName componentName ++ " component") prettyShow+ (buildable biA) (buildable biB)++ -- @build-tool-depends@+ checkDependencies componentName+ (buildToolDepends biA)+ (buildToolDepends biB)++ -- @build-tools@+ checkDependencies componentName+ (buildTools biA)+ (buildTools biB)++ -- @pkgconfig-depends@+ checkDependencies componentName+ (pkgconfigDepends biA)+ (pkgconfigDepends biB)++ checkSame "Cannot change build information (just the dependency version constraints)"+ (biA { targetBuildDepends = [], otherExtensions = [], buildTools = [], buildToolDepends = [], pkgconfigDepends = [], buildable = True })+ (biB { targetBuildDepends = [], otherExtensions = [], buildTools = [], buildToolDepends = [], pkgconfigDepends = [], buildable = True })++changesOk' :: Eq a => Severity -> String -> (a -> String) -> Check a+changesOk' rel what render a b+ | a == b = return ()+ | otherwise = logChange (Change rel what (render a) (render b))++changesOk :: Eq a => String -> (a -> String) -> Check a+changesOk = changesOk' Normal++changesOkList :: (String -> (a -> String) -> Check a)+ -> String -> (a -> String) -> Check [a]+changesOkList changesOkElem what render = go+ where+ go [] [] = return ()+ go (a:_) [] = logChange (Change Normal ("removed " ++ what) (render a) "")+ go [] (b:_) = logChange (Change Normal ("added " ++ what) "" (render b))+ go (a:as) (b:bs) = changesOkElem what render a b >> go as bs++changesOkSet :: Ord a => String -> (a -> String) -> Check (Set.Set a)+changesOkSet what render old new = do+ unless (Set.null removed) $+ logChange (Change Normal ("removed " ++ what) (renderSet removed) "")+ unless (Set.null added) $+ logChange (Change Normal ("added " ++ what) "" (renderSet added))+ return ()+ where+ added = new Set.\\ old+ removed = old Set.\\ new+ renderSet = intercalate ", " . map render . Set.toList+++-- | Single-quote-wrapping 'prettyShow'+prettyShow'' :: Pretty a => a -> String+prettyShow'' x = "'" ++ prettyShow x ++ "'"++checkSame :: Eq a => String -> Check a+checkSame msg x y | x == y = return ()+ | otherwise = fail msg++checkList :: String -> Check a -> Check [a]+checkList _ _ [] [] = return ()+checkList msg checkElem (x:xs) (y:ys) = checkElem x y+ >> checkList msg checkElem xs ys+checkList msg _ _ _ = fail msg++checkListAssoc :: Eq b => String -> Check a -> Check [(b,a)]+checkListAssoc _ _ [] [] = return ()+checkListAssoc msg checkElem ((kx,x):xs) ((ky,y):ys)+ | kx == ky = checkElem x y+ >> checkListAssoc msg checkElem xs ys+ | otherwise = fail msg+checkListAssoc msg _ _ _ = fail msg++checkNotPresent :: String -> Check (Maybe String)+checkNotPresent msg _ (Just _) = fail msg+checkNotPresent _ _ Nothing = return ()++checkMaybe :: String -> Check a -> Check (Maybe a)+checkMaybe _ _ Nothing Nothing = return ()+checkMaybe _ check (Just x) (Just y) = check x y+checkMaybe msg _ _ _ = fail msg++ppTestedWith :: [(CompilerFlavor, VersionRange)] -> Doc+ppTestedWith = hsep . punctuate colon . map (uncurry ppPair)+ where+ ppPair compiler vr = text (prettyShow compiler) <+> text (prettyShow vr)++ppSourceRepo :: SourceRepo -> PrettyField ()+ppSourceRepo repo = PrettySection () "source-repository" [pretty kind] $+ prettyFieldGrammar cabalSpecLatest (sourceRepoFieldGrammar kind) repo+ where+ kind = repoKind repo++-- TODO: Verify that we don't need to worry about UTF8+-- | Insert or update \"x-revision:\" field+insertRevisionField :: Int -> ByteString -> ByteString+insertRevisionField rev+ | rev == 1 = LBS8.unlines . insertAfterVersion . LBS8.lines+ | otherwise = LBS8.unlines . replaceRevision . LBS8.lines+ where+ replaceRevision [] = []+ replaceRevision (ln:lns)+ | isField (LBS8.pack "x-revision") ln+ = LBS8.pack ("x-revision: " ++ show rev) : lns++ | otherwise+ = ln : replaceRevision lns++ insertAfterVersion [] = []+ insertAfterVersion (ln:lns)+ | isField (LBS8.pack "version") ln+ = ln : LBS8.pack ("x-revision: " ++ show rev) : lns++ | otherwise+ = ln : insertAfterVersion lns++ isField nm ln+ | LBS8.isPrefixOf nm (LBS8.map Char.toLower ln)+ , let (_, t) = LBS8.span (\c -> c == ' ' || c == '\t')+ (LBS8.drop (LBS8.length nm) ln)+ , Just (':',_) <- LBS8.uncons t+ = True+ | otherwise = False
+ src/CabalEdit.hs view
@@ -0,0 +1,98 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Copyright : Herbert Valerio Riedel+-- SPDX-License-Identifier: GPL-3.0-or-later+module CabalEdit+ ( cabalEditXRev+ , cabalSplitAtField+ , PkgRev+ ) where++import Data.ByteString (ByteString)+import qualified Data.ByteString.Char8 as BS8+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup ((<>))+#endif+import qualified Distribution.Fields.Field as C+import qualified Distribution.Fields.Parser as C+import qualified Distribution.Parsec.Position as C (Position(Position))++type PkgRev = Word++-- | Insert or replace existing "x-revision" line+--+-- __NOTE__: This uses 'cabalSplitAtField' and therefore currently+-- supports only simplified (i.e. without use of @{@ @}@ layout+-- tokens) Cabal file grammar+cabalEditXRev :: PkgRev -> ByteString -> ByteString+cabalEditXRev xrev oldcab = pre1 <> mid1 <> xrevLine <> post1+ where+ (pre0,_,post0) = either (error . show) id $ cabalSplitAtField "x-revision" oldcab+ (pre1,mid1,post1) = either (error . show) id $ cabalSplitAtField "version" (pre0 <> post0)++ ls = BS8.lines oldcab++ xrevLine = "x-revision: " <> BS8.pack (show xrev) <> if isCRLF then "\r\n" else "\n"++ -- simple heuristic+ isCRLF = case ls of+ [] -> False+ ("":_) -> False+ (l1:_) -> BS8.last l1 == '\r'+++-- | Split a cabal file into three fragments: before the (first+-- occurence of a field), the field, and the rest after the field.+--+-- This should, in spirit, follow the invariant (assuming there are no parsing failures):+--+-- > cabalSplitAtField someField (pre <> mid <> post) == Right (pre, mid, post)+--+-- If field not found, the middle and trailing fragment will be empty 'ByteString's+--+-- This operation is quite universal; it can be used to remove fields,+-- or insert content before a field or after a field etc+--+-- __NOTE__: This doesn't properly handle layout-mode (i.e. as controlled by the @{@ and @}@ tokens) yet+cabalSplitAtField :: ByteString -- ^ fieldname+ -> ByteString -- ^ @.cabal@ description+ -> Either String (ByteString,ByteString,ByteString) -- ^ pre, mid, post+cabalSplitAtField fname cab0 = do+ fields <- either (Left . show) Right $ C.readFields cab0++ case [ (npos, vals) | C.Field (C.Name (C.Position npos _) n) vals <- fields, n == fname ] of+ [] -> pure (cab0, BS8.empty, BS8.empty)+ (npos, vals):_ -> case vals of+ [] -> pure $! go (npos-1) 1++ _ -> let C.FieldLine (C.Position pos2 _) _ = last vals+ in pure $! go (npos-1) (1+pos2-npos)++ where+ cab0lines = BS8.lines cab0++ -- TODO: detect '{'s in `mid` and fixup k to include the closing '}'+ --+ -- NB: it's not enough to simply look at the next field's line+ -- number, as we don't want 'mid' to include trailing comments+ -- which conventionally belong to the next fields; i.e. in case of+ -- something like+ --+ -- name: foo+ -- version: 1.2.3+ -- x-revision: {+ -- 42+ -- }+ -- -- for reasons this must remain Custom instead of Simple+ -- build-type: Custm+ -- synopsis: ...+ --+ -- We want the middle of 'cabalSplitAtField "x-revision"' to stop+ -- right after the '}' and *not* include the comment+ --+ go j k = (BS8.unlines pre, BS8.unlines mid, BS8.unlines post)+ where+ (pre, midpost) = splitAt j cab0lines+ (mid,post) = splitAt k midpost
+ src/IndexShaSum.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}++-- |+-- Module : IndexShaSum+-- Copyright : Herbert Valerio Riedel+-- SPDX-License-Identifier: GPL-3.0-or-later+--+module IndexShaSum (run, IndexShaSumOptions(..)) where++import qualified Codec.Archive.Tar as Tar+import qualified Codec.Compression.GZip as GZip+import Control.Monad+import qualified Data.Aeson as J+import qualified Data.Aeson.Types as J+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Short as BSS+import Data.Maybe+import Data.Set (Set)+import qualified Data.Set as Set+import Data.String+import Data.Text (Text)+import Data.Text.Encoding as T+import System.FilePath++#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup ((<>))+#endif++#if MIN_VERSION_aeson(2,0,0)+import qualified Data.Aeson.Key as Key+import qualified Data.Aeson.KeyMap as KeyMap+type Key = J.Key++keyToText :: Key -> Text+keyToText = Key.toText+#else+import qualified Data.HashMap.Strict as KeyMap+type Key = Text++keyToText :: Key -> Text+keyToText = id+#endif++data IndexShaSumOptions = IndexShaSumOptions+ { optFlatStyle :: Bool+ , optISSIndexTar :: FilePath+ , optBaseDir :: Maybe FilePath+ } deriving Show++type SrcTarName = BSS.ShortByteString -- with .tar.gz suffix+type SrcTarSha256 = BSS.ShortByteString -- base16++run :: IndexShaSumOptions -> IO ()+run (IndexShaSumOptions {..}) = do+ idx <- readTarEntries optISSIndexTar+ forM_ (collect idx) (uncurry printSumLine)+ where+ printSumLine fn sh256 = BS.putStr line+ where+ line = mconcat [BSS.fromShort (fixupSum sh256), " " , bdirpfx, BSS.fromShort fn', "\n"]+ bdirpfx = maybe "" fromString optBaseDir+ fn' = if optFlatStyle then fn else unFlat fn++ -- | Missing checksums are denoted by a 0-checksum+ fixupSum x+ | BSS.null x = BSS.toShort (BS.replicate 64 48)+ | otherwise = x++ collect :: [Tar.Entry] -> [(SrcTarName,SrcTarSha256)]+ collect = go mempty mempty++ go :: Set SrcTarName -> Set SrcTarName -> [Tar.Entry] -> [(SrcTarName,SrcTarSha256)]+ go !seen1 !seen2 []+ | missingCabs <- Set.difference seen1 seen2+ , not (Set.null missingCabs) = error "missing .cabal file(s)"+ | otherwise -- append files with missing checksum+ = [ (missingSum, "") | missingSum <- Set.toList (Set.difference seen2 seen1) ]+ go !seen1 !seen2 (e:es)+ | takeExtension fn == ".cabal"+ , [pn,pv,_cn] <- splitDirectories fn+ = let fn' = fromString (pn ++ "-" ++ pv ++ ".tar.gz")+ in go seen1 (Set.insert fn' seen2) es+ | takeFileName fn == "package.json"+ , Tar.NormalFile bs _sz <- Tar.entryContent e+ = let (fn',cksum) = fromMaybe undefined (decodePkgJsonFile bs)+ in if Set.member fn' seen1+ then go seen1 seen2 es+ else ((fn',cksum) : go (Set.insert fn' seen1) seen2 es)+ | otherwise = go seen1 seen2 es+ where+ fn = Tar.entryPath e++-- | Convert to non-flat layout (i.e. @<name>/<ver>/<name>-<ver>.tar.gz@)+unFlat :: SrcTarName -> SrcTarName+unFlat fn0 = BSS.toShort $ mconcat [pn <> "/" <> pv <> "/" <> fn0']+ where+ fn0' = BSS.fromShort fn0++ Just base = stripSuffixBS ".tar.gz" fn0'++ (pn_, pv) = BS.spanEnd (\c -> (c >= 0x30 && c <= 0x3a) || c == 0x2e) base+ Just (pn, 0x2d) = BS.unsnoc pn_++-- | Read tarball lazily (and possibly decompress)+readTarEntries :: FilePath -> IO [Tar.Entry]+readTarEntries idxtar = do+ es <- case takeExtension idxtar of+ ".gz" -> Tar.read . GZip.decompress <$> BSL.readFile idxtar+ ".tar" -> Tar.read <$> BSL.readFile idxtar+ ext -> error ("unknown extension " ++ show ext)++ return (Tar.foldEntries (:) [] (\err -> error ("readTarEntries " ++ show err)) es)++-- | Decode and extract source-tarball filename and sha256 checksum from TUF @package.json@+decodePkgJsonFile :: BSL.ByteString -> Maybe (SrcTarName, SrcTarSha256)+decodePkgJsonFile bs = do+ metainfo <- J.decode' bs+ [(fn,s256)] <- packagejson2sha metainfo++ return $! strictPair (BSS.toShort $ normaliseFn fn) (BSS.toShort s256)+ where+ normaliseFn fn = fromMaybe fn $ stripPrefixBS "<repo>/package/" fn++ packagejson2sha :: J.Value -> Maybe [(ByteString, ByteString)]+ packagejson2sha = J.parseMaybe go1+ where+ go1 :: J.Value -> J.Parser [(ByteString, ByteString)]+ go1 = J.withObject "PackageJson" $ \o -> do+ signed <- o J..: "signed"+ targets <- signed J..: "targets"+ J.withObject "PackageJson.signed.targets" go2 targets++ go2 :: J.Object -> J.Parser [(ByteString, ByteString)]+ go2 m = forM (KeyMap.toList m) $ \(k,v) -> do+ J.withObject ".targets{}" (go3 k) v++ go3 :: Key -> J.Object -> J.Parser (ByteString, ByteString)+ go3 k o = do+ hashes <- o J..: "hashes"+ sh256 <- hashes J..: "sha256"+ return (T.encodeUtf8 (keyToText k), T.encodeUtf8 sh256)++strictPair :: a -> b -> (a,b)+strictPair !a !b = (a,b)++stripPrefixBS :: ByteString -> ByteString -> Maybe ByteString+stripPrefixBS pfx b+ | BS.isPrefixOf pfx b = Just $ BS.drop (BS.length pfx) b+ | otherwise = Nothing+++stripSuffixBS :: ByteString -> ByteString -> Maybe ByteString+stripSuffixBS sfx b+ | BS.isSuffixOf sfx b = Just $ BS.take (BS.length b - BS.length sfx) b+ | otherwise = Nothing
+ src/Main.hs view
@@ -0,0 +1,958 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Main+-- Copyright : Herbert Valerio Riedel, Andreas Abel+-- SPDX-License-Identifier: GPL-3.0-or-later+--+module Main where++import qualified Data.Aeson as J+import qualified Data.Aeson.Types as J+import qualified Data.ByteString.Builder as Builder+import Control.DeepSeq+import Control.Exception+import Control.Monad+import Control.Monad.State.Strict+import Data.Bits+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BS8+import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Search as BSS+import Data.Char (isSpace)+import Data.Foldable (toList)+import qualified Data.List as List+import Data.Maybe+import Data.Time.Clock.POSIX (getPOSIXTime)+#if !MIN_VERSION_base(4,11,0)+import Data.Semigroup (Semigroup(..))+#endif++import qualified Distribution.Package as C+import qualified Distribution.PackageDescription as C+import qualified Distribution.PackageDescription.Parsec as C+import qualified Distribution.Parsec as C+import qualified Distribution.Fields as C+import qualified Distribution.Fields.Field as C (fieldAnn)+import qualified Distribution.Pretty as C+import qualified Distribution.Verbosity as C+import qualified Distribution.Version as C+import qualified Distribution.Text as C++import Lens.Micro+import Lens.Micro.Mtl+import Lens.Micro.TH++import Network.Http.Client+import Network.NetRc+import Numeric.Natural (Natural)+import Options.Applicative as OA+import System.Directory+import System.Environment (lookupEnv)+import System.Exit (ExitCode (..), exitFailure)+import System.FilePath+import System.IO.Error (tryIOError, isDoesNotExistError)+import qualified System.IO.Streams as Streams+import System.Process.ByteString (readProcessWithExitCode)+import Text.HTML.TagSoup+import Text.Printf (printf)+import qualified Paths_hackage_cli+import qualified Data.Version as V++import qualified Distribution.Types.BuildInfo.Lens as LC+import qualified Distribution.Types.GenericPackageDescription.Lens as LC++-- import Cabal++import Distribution.Server.Util.CabalRevisions+import IndexShaSum+import CabalEdit++type PkgName = ByteString+type PkgVer = ByteString++showVersion :: C.Version -> String+showVersion = C.display++pkgVerToVersion :: PkgVer -> C.Version+pkgVerToVersion = fromMaybe (error "invalid version") . C.simpleParse . BS8.unpack++pkgVerInRange :: PkgVer -> C.VersionRange -> Bool+pkgVerInRange v vr = pkgVerToVersion v `C.withinRange` vr++type HIO = StateT HConn IO++data HConn = HConn+ { _hcMkConn :: IO Connection+ , _hcConn :: Maybe Connection+ , _hcReqCnt :: Natural -- ^ requests submitted on current 'Connection'+ , _hcRspCnt :: Natural -- ^ responses read from current 'Connection'+ }++makeLenses ''HConn++-- | Requests that can be issued in current connection before exhausting the 50-req/conn server limit+hcReqLeft :: SimpleGetter HConn Natural -- (Natural -> f Natural) -> HConn -> f HConn+hcReqLeft = hcReqCnt . to f+ where+ f n | n > lim = 0+ | otherwise = lim - n+ lim = 50++setUA :: RequestBuilder ()+setUA = setHeader "User-Agent" uaStr+ where+ uaStr = "hackage-cli/" <> BS8.pack (V.showVersion Paths_hackage_cli.version)++hackageSendGET :: ByteString -> ByteString -> HIO ()+hackageSendGET p a = do+ q1 <- liftIO $ buildRequest $ do+ http GET p+ setUA+ setAccept a++ lft <- use hcReqLeft+ unless (lft > 0) $+ fail "hackageSendGET: request budget exhausted for current connection"++ c <- openHConn+ liftIO $ sendRequest c q1 emptyBody+ hcReqCnt += 1++hackagePutTgz :: ByteString -> ByteString -> HIO ByteString+hackagePutTgz p tgz = do+ q1 <- liftIO $ buildRequest $ do+ http PUT p+ setUA+ -- setAccept "application/json" -- wishful thinking+ setContentType "application/x-tar"+ -- setContentEncoding "gzip"+ setContentLength (fromIntegral $ BS.length tgz)++ lft <- use hcReqLeft+ unless (lft > 0) $+ fail "hackagePutTgz: request budget exhausted for current connection"++ c <- openHConn+ liftIO $ sendRequest c q1 (bsBody tgz)+ resp <- liftIO $ try (receiveResponse c concatHandler')+ closeHConn+ hcReqCnt += 1++ case resp of+ Right bs -> -- do+ -- liftIO $ BS.writeFile "raw.out" bs+ return bs++ Left e@HttpClientError {} -> -- do+ return (BS8.pack $ show e)++hackageRecvResp :: HIO ByteString+hackageRecvResp = do+ c <- openHConn+ reqCnt <- use hcReqCnt+ rspCnt <- use hcRspCnt+ unless (reqCnt > rspCnt) $+ fail "hackageRecvResp: not response available to receive"++ resp <- liftIO $ receiveResponse c concatHandler'+ hcRspCnt += 1++ return resp++-- | Whether to operate in review/test mode or publish the revision for real+data DryWetRun = DryRun -- ^ review/test mode+ | WetRun -- ^ publish++hackagePostCabal :: (ByteString,ByteString) -> (PkgName,PkgVer) -> ByteString -> DryWetRun -> HIO ByteString+hackagePostCabal cred (pkgn,pkgv) rawcab dry = do+ when (boundary `BS.isInfixOf` rawcab) $ fail "WTF... cabal file contains boundary-pattern"++ q1 <- liftIO $ buildRequest $ do+ http POST urlpath+ setUA+ uncurry setAuthorizationBasic cred+ setAccept "application/json" -- wishful thinking+ setContentType ("multipart/form-data; boundary="<>boundary) -- RFC2388+ setContentLength bodyLen++ c <- reOpenHConn++ liftIO $ sendRequest c q1 (bsBody body)++ resp <- liftIO $ try (receiveResponse c concatHandler')+ closeHConn++ case resp of+ Right bs -> -- do+ -- liftIO $ BS.writeFile "raw.out" bs+ return (BS8.unlines [ h2 <> ":\n" <> renderTags ts | (h2, ts) <- scrape200 bs ])++ Left e@HttpClientError {} -> -- do+ -- Hackage currently timeouts w/ 503 guru meditation errors,+ -- which usually means that the transaction has succeeded+ -- liftIO $ BS.writeFile "raw.out" bs+ return (BS8.pack $ show e)+ where+ urlpath = mconcat [ "/package/", pkgn, "-", pkgv, "/", pkgn, ".cabal/edit" ]++ isDry DryRun = True+ isDry WetRun = False++ body = Builder.toLazyByteString $ multiPartBuilder boundary+ [ ("cabalfile",[],[],rawcab)+ , if isDry dry+ then ("review", [],[],"Review changes")+ else ("publish",[],[],"Publish new revision")+ ]++ bodyLen = fromIntegral $ BSL.length body++ boundary = "4d5bb1565a084d78868ff0178bdf4f61"++ -- scrape200 :: ByteString -> (Bool, h2parts)+ scrape200 html = h2parts+ where+ tags = parseTags (html :: ByteString)++ h2parts = [ (t,map cleanText $ takeWhile (/= TagClose "form") xs)+ | (TagOpen "h2" _: TagText t: TagClose "h2": xs) <- partitions (== TagOpen "h2" []) tags+ , t /= "Advice on adjusting version constraints" ]++ cleanText (TagText t)+ | t' == "", '\n' `BS8.elem` t = TagText "\n"+ | otherwise = TagText t+ where+ t' = fst . BS8.spanEnd (=='\n') . BS8.dropWhile (=='\n') $ t+ cleanText x = x++class ToBuilder a where+ toBuilder :: a -> Builder.Builder++instance ToBuilder ByteString where+ toBuilder = Builder.byteString++instance ToBuilder BSL.ByteString where+ toBuilder = Builder.lazyByteString++bsBody :: ToBuilder a => a -> Streams.OutputStream Builder.Builder -> IO ()+bsBody bs = Streams.write (Just (toBuilder bs))++-- | Upload a candidate to Hackage+--+-- This is a bit overkill, as one could easily just use @curl(1)@ for this:+--+-- > curl --form package=@"$PKGID".tar.gz -u "${CREDS}" https://hackage.haskell.org/packages/candidates/+--+hackagePushCandidate :: (ByteString,ByteString) -> (FilePath,ByteString) -> HIO ByteString+hackagePushCandidate cred (tarname,rawtarball) = do+ when (boundary `BS.isInfixOf` rawtarball) $ fail "WTF... tarball contains boundary-pattern"++ q1 <- liftIO $ buildRequest $ do+ http POST urlpath+ setUA+ uncurry setAuthorizationBasic cred+ setAccept "application/json" -- wishful thinking+ setContentType ("multipart/form-data; boundary="<>boundary) -- RFC2388+ setContentLength bodyLen++ c <- reOpenHConn++ liftIO $ sendRequest c q1 (bsBody body)++ resp <- liftIO $ try (receiveResponse c (\r is -> (,) r <$> concatHandler r is))+ closeHConn++ case resp of+ Right (rc,bs) -> do+ return (BS8.pack (show rc) <> bs)+ Left (HttpClientError code bs) -> return (BS8.pack ("code=" <> show code <> "\n") <> bs)+ -- Hackage currently timeouts w/ 503 guru meditation errors,+ -- which usually means that the transaction has succeeded+ where+ urlpath = "/packages/candidates/"++ body = Builder.toLazyByteString $+ multiPartBuilder boundary [ ("package", [("filename", BS8.pack tarname)]+ , ["Content-Type: application/gzip"], rawtarball)]+ bodyLen = fromIntegral $ BSL.length body++ boundary = "4d5bb1565a084d78868ff0178bdf4f61"++-- | Simplified RFC2388 multipart/form-data formatter+--+-- TODO: make a streaming-variant+multiPartBuilder :: ByteString -> [(ByteString,[(ByteString,ByteString)],[ByteString],ByteString)] -> Builder.Builder+multiPartBuilder boundary mparts = mconcat $ concatMap mkPart mparts ++ trailer+ where+ mkPart (name, xprops, xhdrs, payload)+ = [ dash, bs boundary, crlf+ , bs"Content-Disposition: form-data; name=\"", bs name, bs"\""+ ] +++ concat [ [bs "; ", bs k, bs"=\"", bs v, bs"\"" ] | (k,v) <- xprops ] ++ [ crlf ] +++ concat [ [bs h, crlf] | h <- xhdrs ] +++ [ crlf+ , bs payload, crlf+ ]++ trailer = [ dash, bs boundary, dash, crlf ]++ crlf = bs"\r\n"+ dash = bs"--"+ bs = Builder.byteString++fetchVersions :: PkgName -> HIO [(PkgVer,PkgVerStatus)]+fetchVersions pkgn = do+ hackageSendGET ("/package/" <> pkgn <> "/preferred") "application/json"+ resp <- hackageRecvResp+ case decodeVersions resp of+ Right xs -> pure xs+ Left err -> fail err++fetchCabalFile :: PkgName -> PkgVer -> HIO ByteString+fetchCabalFile pkgn pkgv = do+ hackageSendGET urlpath "text/plain"+ hackageRecvResp+ where+ urlpath = mconcat ["/package/", pkgn, "-", pkgv, "/", pkgn, ".cabal"]++fetchCabalFiles :: PkgName -> [PkgVer] -> HIO [(PkgVer,ByteString)]+fetchCabalFiles pkgn pkgvs0 = do+ -- HTTP pipelining+ tmp <- go [] pkgvs0+ return (concat . reverse $ tmp)+ where+ go acc [] = pure acc+ go acc vs0 = do+ (_,lft) <- getHConn+ let (vs,vs') = nsplitAt lft vs0+ when (null vs) $ fail "fetchCabalFiles: the impossible happened"++ -- HTTP-pipeline requests; compensates a bit for SSL-induced latency+ mcabs <- forM (mkPipeline 4 vs) $ \case+ Left pkgv -> do -- request+ let urlpath = mconcat ["/package/", pkgn, "-", pkgv, "/", pkgn, ".cabal"]+ -- liftIO $ putStrLn $ show urlpath+ hackageSendGET urlpath "text/plain"+ return Nothing++ Right pkgv -> do -- response+ -- liftIO $ putStrLn ("read " ++ show pkgv)+ resp <- hackageRecvResp+ return $ Just (pkgv, resp)++ go (catMaybes mcabs : acc) vs'++-- Left means request; Right means receive+mkPipeline :: Natural -> [a] -> [Either a a]+mkPipeline maxQ vs+ | not postCond = error "mkPipeline: internal error" -- paranoia+ | otherwise = concat [ map Left rqs1+ , concat [ [Left v1, Right v2] | (v1,v2) <- zip rqs2 res2 ]+ , map Right res3+ ]+ where+ (rqs1,rqs2) = nsplitAt n vs+ (res2,res3) = nsplitAt m vs++ postCond = sameLen rqs2 res2 && sameLen rqs1 res3++ l = nlength vs+ n = min l maxQ+ m = l-n++ sameLen [] [] = True+ sameLen (_:xs) (_:ys) = sameLen xs ys+ sameLen [] (_:_) = False+ sameLen (_:_) [] = False++fetchAllCabalFiles :: PkgName -> C.VersionRange -> HIO [(PkgVer,Maybe ByteString)]+fetchAllCabalFiles pkgn vrange = do+ vs <- fetchVersions pkgn+ liftIO $ putStrLn ("Found " ++ show (length vs) ++ " package versions for " ++ show pkgn ++ ", downloading now...")++ let (wanted,unwanted) = List.partition (`pkgVerInRange` vrange) (map fst vs)++ fetched <- map (fmap Just) <$> fetchCabalFiles pkgn wanted+ pure (List.sortOn (pkgVerToVersion . fst) (fetched ++ [ (v,Nothing) | v <- unwanted ]))++data PkgVerStatus = Normal | UnPreferred | Deprecated deriving (Eq,Show)+instance NFData PkgVerStatus where rnf !_ = ()++decodeVersions :: ByteString -> Either String [(PkgVer,PkgVerStatus)]+decodeVersions bs = do+ obj <- J.eitherDecode' (BSL.fromStrict bs)+ flip J.parseEither obj $ \o -> do+ normal <- o J..:? "normal-version"+ unpreferred <- o J..:? "unpreferred-version"+ deprecated <- o J..:? "deprecated-version"+ pure $ toPairs Normal normal ++ toPairs UnPreferred unpreferred ++ toPairs Deprecated deprecated+ where+ toPairs :: PkgVerStatus -> Maybe [String] -> [(PkgVer,PkgVerStatus)]+ toPairs s (Just vs) = [(BS8.pack v, s) | v <- vs]+ toPairs _ _ = []++closeHConn :: HIO ()+closeHConn = do+ mhc <- use hcConn+ forM_ mhc $ \hc -> do+ liftIO $ closeConnection hc+ hcConn .= Nothing++ reqCnt <- use hcReqCnt+ rspCnt <- use hcRspCnt+ unless (reqCnt == rspCnt) $+ liftIO $ putStrLn $ concat ["warning: req-cnt=", show reqCnt, " rsp-cnt=", show rspCnt]++ hcReqCnt .= 0+ hcRspCnt .= 0++openHConn :: HIO Connection+openHConn = use hcConn >>= \case+ Just c -> return c+ Nothing -> do+ mkConn <- use hcMkConn+ c <- liftIO mkConn+ hcConn .= Just c+ hcReqCnt .= 0 -- redundant+ hcRspCnt .= 0 -- redundant+ return c++reOpenHConn :: HIO Connection+reOpenHConn = closeHConn >> openHConn++getHConn :: HIO (Connection,Natural)+getHConn = do+ lft <- use hcReqLeft+ c <- if lft > 0 then openHConn else reOpenHConn+ (,) c <$> use hcReqLeft++nlength :: [a] -> Natural+nlength = fromIntegral . length++nsplitAt :: Natural -> [a] -> ([a],[a])+nsplitAt n = splitAt i+ where+ i = fromMaybe (error "nsplitAt: overflow") $ toIntegralSized n++findLibrarySection :: [C.Field C.Position] -> Maybe (Int, Int)+findLibrarySection [] = Nothing+findLibrarySection (C.Section (C.Name (C.Position row _) "library") [] fs : _) =+ Just (row, findIndent fs)+ where+ findIndent [] = 4+ findIndent (f : _) = case C.fieldAnn f of+ C.Position _ col -> pred col+findLibrarySection (_ : fs) = findLibrarySection fs++----------------------------------------------------------------------------+-- CLI Interface++data Options = Options+ { optVerbose :: !Bool+ , optHost :: !Hostname+ , optCommand :: !Command+ } deriving Show++data PullCOptions = PullCOptions+ { optPlCPkgName :: !PkgName+ , optPlCIncrRev :: !Bool+ , optPlCForce :: !Bool+ , optPlCPkgVers :: Maybe C.VersionRange+ } deriving Show++data SyncCOptions = SyncCOptions+ { optSyCFile :: FilePath+ , optSyCIncrRev :: !Bool+ , optSyCForce :: !Bool+ } deriving Show++data ListCOptions = ListCOptions+ { optLCPkgName :: !PkgName+ , optNoAnn :: !Bool+ , optRevUrls :: !Bool+ } deriving Show++data PushCOptions = PushCOptions+ { optPsCIncrRev :: !Bool+ , optPsCPublish :: !Bool+ , optPsCFiles :: [FilePath]+ } deriving Show++data PushPCOptions = PushPCOptions+ { optPPCFiles :: [FilePath]+ } deriving Show++data CheckROptions = CheckROptions+ { optCRNew :: FilePath+ , optCROrig :: FilePath+ } deriving Show++data AddBoundOptions = AddBoundOptions+ { optABPackageName :: C.PackageName+ , optABVersionRange :: C.VersionRange+ , optForce :: Bool -- ^ Disable the check whether bound is subsumed by existing constraints.+ , optABMessage :: [String]+ , optABFiles :: [FilePath]+ } deriving Show++data Command+ = ListCabal !ListCOptions+ | PullCabal !PullCOptions+ | PushCabal !PushCOptions+ | SyncCabal !SyncCOptions+ | PushCandidate !PushPCOptions+ | CheckRevision !CheckROptions+ | IndexShaSum !IndexShaSumOptions+ | AddBound !AddBoundOptions+ deriving Show++optionsParserInfo :: ParserInfo Options+optionsParserInfo+ = info (helper <*> verOption <*> oParser)+ (fullDesc+ <> header "hackage-cli - CLI tool for Hackage"+ <> footer footerStr)+ where+ footerStr = unwords+ [ "Each command has a sub-`--help` text. Hackage credentials are expected to be"+ , "stored in an `${HOME}/.netrc`-entry (or `.netrc.gpg`) for the respective Hackage hostname."+ , "E.g. \"machine hackage.haskell.org login MyUserName password TrustNo\"."+ , "All interactions with Hackage occur TLS-encrypted via the HTTPS protocol."+ ]++ bstr = BS8.pack <$> str++ prsc :: C.Parsec s => OA.ReadM s+ prsc = OA.eitherReader C.eitherParsec++ vrange = do+ s <- str+ case C.simpleParse s of+ Nothing -> fail "invalid version range"+ Just vr -> pure vr++ listcoParser = ListCabal <$>+ (ListCOptions <$> OA.argument bstr (metavar "PKGNAME" <> action "file")+ <*> switch (long "no-annotations" <> help "don't add preferred-versions annotations")+ <*> switch (long "rev-urls" <> help "list revision URLs"))++ pullcoParser = PullCabal <$>+ (PullCOptions <$> OA.argument bstr (metavar "PKGNAME" <> action "file")+ <*> switch (long "incr-rev" <> help "increment x-revision field")+ <*> switch (long "force" <> help "force overwriting existing files")+ <*> optional (OA.argument vrange (metavar "VERSION-CONSTRAINT")))++ synccoParser = SyncCabal <$>+ (SyncCOptions <$> OA.argument str (metavar "CABALFILE" <> action "file")+ <*> switch (long "incr-rev" <> help "increment x-revision field")+ <*> switch (long "force" <> help "force overwriting local file with older revision"))++ pushcoParser = PushCabal <$> (PushCOptions+ <$> switch (long "incr-rev" <> help "increment x-revision field")+ <*> switch (long "publish" <> help "publish revision (review-mode)")+ <*> some (OA.argument str (metavar "CABALFILES..." <> action "file")))++ pushpcoParser = PushCandidate <$> (PushPCOptions <$> some (OA.argument str (metavar "TARBALLS..." <> action "file")))++ checkrevParsser = CheckRevision <$> (CheckROptions <$> OA.argument str (metavar "NEWCABAL" <> action "file")+ <*> OA.argument str (metavar "OLDCABAL" <> action "file"))+++ indexssParser = IndexShaSum <$> (IndexShaSumOptions <$> switch (long "flat" <> help "flat filesystem layout (used by mirrors)")+ <*> OA.argument str (metavar "INDEX-TAR" <> action "file")+ <*> optional (OA.argument str (metavar "BASEDIR" <> action "directory")))++ addboundParser = AddBound <$> (AddBoundOptions <$> OA.argument prsc (metavar "DEPENDENCY")+ <*> OA.argument prsc (metavar "VERSIONRANGE")+ <*> OA.switch (long "force" <> help "Add bound even if it is already subsumed by existing constraints.")+ <*> many (OA.option str (OA.short 'm' <> OA.long "message" <> metavar "MSG" <> help "Use given MSG as a comment. If multiple -m options are given, their values are concatenated with 'unlines'."))+ <*> some (OA.argument str (metavar "CABALFILES..." <> action "file")))++ oParser+ = Options <$> switch (long "verbose" <> help "Enable verbose output.")+ <*> option bstr (long "hostname" <> metavar "HOSTNAME" <> value "hackage.haskell.org"+ <> help "Hackage hostname" <> showDefault)+ <*> subparser (mconcat [ command "pull-cabal" (info (helper <*> pullcoParser)+ (progDesc "Download .cabal files for a package."))+ , command "push-cabal" (info (helper <*> pushcoParser)+ (progDesc "Upload revised .cabal files."))+ , command "sync-cabal" (info (helper <*> synccoParser)+ (progDesc "Update/sync local .cabal file with latest revision on Hackage."))+ , command "push-candidate" (info (helper <*> pushpcoParser)+ (progDesc "Upload package candidate(s)."))+ , command "list-versions" (info (helper <*> listcoParser)+ (progDesc "List versions for a package."))+ , command "check-revision" (info (helper <*> checkrevParsser)+ (progDesc "Validate revision."))+ , command "index-sha256sum" (info (helper <*> indexssParser)+ (progDesc "Generate sha256sum-format file."))+ , command "add-bound" (info (helper <*> addboundParser)+ (progDesc "Add bound to the library section of a package, unless the bound is redundant. The .cabal file is edited in place."))+ ])++ verOption = infoOption verMsg (long "version" <> help "Output version information and exit.")+ where+ verMsg = "hackage-cli " <> V.showVersion Paths_hackage_cli.version++----------------------------------------------------------------------------++main :: IO ()+main = do+ opts <- execParser optionsParserInfo+ mainWithOptions opts++mainWithOptions :: Options -> IO ()+mainWithOptions Options {..} = do+ case optCommand of+ PullCabal (PullCOptions {..}) -> do+ let pkgn = optPlCPkgName++ cs <- runHConn (fetchAllCabalFiles pkgn (fromMaybe C.anyVersion optPlCPkgVers))++ forM_ cs $ \(v,mraw) -> case mraw of+ Nothing -> putStrLn ("skipped excluded " ++ BS8.unpack v)+ Just raw0 -> do+ let fn = BS8.unpack $ pkgn <> "-" <> v <> ".cabal"++ let raw | optPlCIncrRev = incrXrev raw0+ | otherwise = raw0++ doesFileExist fn >>= \case+ False -> do+ BS.writeFile fn raw+ putStrLn ("saved " ++ fn ++ " (" ++ show (BS.length raw) ++ " bytes)")+ True ->+ if optPlCForce+ then do+ BS.writeFile fn raw+ putStrLn ("overwritten " ++ fn ++ " (" ++ show (BS.length raw) ++ " bytes)")+ else+ putStrLn ("WARNING: skipped existing " ++ fn ++ " (use --force to overwrite)")++ return ()++ SyncCabal (SyncCOptions {..}) -> do+ (pkgn,pkgv,xrev) <- pkgDescToPkgIdXrev <$> C.readGenericPackageDescription C.deafening optSyCFile+ cab0 <- BS.readFile optSyCFile++ BS8.putStrLn $ mconcat [ "local : "+ , pkgn, "-", pkgv, "-r", BS8.pack (show xrev)+ , " ('", BS8.pack optSyCFile, "')"+ ]++ cab' <- runHConn (fetchCabalFile pkgn pkgv)++ let (pkgn',pkgv',xrev') = pkgDescToPkgIdXrev $ parseGenericPackageDescription' cab'++ BS8.putStrLn $ mconcat [ "remote: "+ , pkgn', "-", pkgv', "-r", BS8.pack (show xrev')+ ]++ let cab'' = cabalEditXRev (xrev'+1) cab'+ bakfn = optSyCFile <> "~"++ case () of+ _ | optSyCIncrRev, cab'' == cab0 -> do+ putStrLn "INFO: local and (incremented) latest remote .cabal revision are already identical! Nothing to do."++ _ | not optSyCForce, xrev' < xrev -> do+ putStrLn "ERROR: Local file has higher revision number than Hackage - aborting! (use --force to allow this)"+ exitFailure++ _ | optSyCIncrRev, cab'' /= cab0 -> do+ when (cab' == cab0) $ do+ putStrLn "NOTE: local and (non-incremented) latest remote .cabal revision are identical."++ BS.writeFile bakfn cab0+ putStrLn ("INFO: saved backup of original local file to " <> bakfn)++ BS.writeFile optSyCFile cab''+ BS8.putStrLn $ mconcat [ "local : "+ , pkgn, "-", pkgv, "-r", BS8.pack (show $ xrev'+1)+ , " ('", BS8.pack optSyCFile, "')"+ ]++ _ | cab' == cab0 -> do+ putStrLn "INFO: local and latest remote .cabal revision are already identical! Nothing to do."++ _ -> do+ BS.writeFile bakfn cab0+ putStrLn ("INFO: saved backup of original local file to " <> bakfn)++ BS.writeFile optSyCFile cab'+ BS8.putStrLn $ mconcat [ "local : "+ , pkgn, "-", pkgv, "-r", BS8.pack (show $ xrev')+ , " ('", BS8.pack optSyCFile, "')"+ ]+++ ListCabal (ListCOptions {..}) -> do+ let pkgn = optLCPkgName++ vs <- runHConn (fetchVersions pkgn)++ unless optNoAnn $+ putStrLn $ concat [ "Found ", show (length vs), " package versions for "+ , show pkgn, " ([U]npreferred, [D]eprecated):"+ ]++ if optRevUrls then do+ forM_ vs $ \(v,_) -> do+ let pid = pkgn <> "-" <> v+ BS8.putStrLn $ mconcat [ " - https://hackage.haskell.org/package/", pid, "/revisions/" ]+ else do+ forM_ vs $ \(v,unp) -> do+ let status = case unp of+ _ | optNoAnn -> ""+ Normal -> " "+ Deprecated -> "[D] "+ UnPreferred -> "[U] "+ BS8.putStrLn $ status <> pkgn <> "-" <> v+ return ()++ PushCabal (PushCOptions {..}) -> do+ (username,password) <- maybe (fail "missing Hackage credentials") return =<< getHackageCreds+ putStrLn $ "Using Hackage credentials for username " ++ show username++ forM_ optPsCFiles $ \fn -> do+ (pkgn,pkgv,xrev) <- pkgDescToPkgIdXrev <$> C.readGenericPackageDescription C.deafening fn+ putStrLn $ concat [ "Pushing ", show fn+ , " (", BS8.unpack pkgn, "-", BS8.unpack pkgv, "~", show xrev, ")"+ , if not optPsCPublish then " [review-mode]" else "", " ..."+ ]++ let editCab | optPsCIncrRev = cabalEditXRev (xrev+1)+ | otherwise = id++ rawcab <- editCab <$> BS.readFile fn+ (dt,tmp) <- timeIt $ runHConn (hackagePostCabal (username,password) (pkgn,pkgv) rawcab+ (if optPsCPublish then WetRun else DryRun))++ printf "Hackage response was (after %.3f secs):\n" dt+ putStrLn (replicate 80 '=')+ BS8.putStrLn (tidyHtml tmp)+ putStrLn (replicate 80 '=')++ PushCandidate (PushPCOptions {..}) -> do+ (username,password) <- maybe (fail "missing Hackage credentials") return =<< getHackageCreds+ putStrLn $ "Using Hackage credentials for username " ++ show username++ forM_ optPPCFiles $ \fn -> do+ putStrLn $ "reading " ++ show fn ++ " ..."+ rawtar <- BS.readFile fn+ putStrLn $ "uplading to Hackage..."+ tmp <- runHConn (hackagePushCandidate (username,password) (takeFileName fn, rawtar))++ putStrLn "Hackage response was:"+ putStrLn (replicate 80 '=')+ BS8.putStrLn tmp+ putStrLn (replicate 80 '=')+++ CheckRevision (CheckROptions {..}) -> do+ old <- BS.readFile optCROrig+ new <- BS.readFile optCRNew++ case diffCabalRevisions old new of+ Left err -> do+ putStrLn "change not allowed:"+ putStrLn err+ exitFailure++ Right [] -> do+ putStrLn "no-op change detected"+ exitFailure++ Right changes -> do+ putStrLn "change allowed:"+ forM_ changes $ \(Change _ what old' new') -> do+ putStrLn $ "what: " ++ what+ putStrLn $ " old: " ++ old'+ putStrLn $ " new: " ++ new'++ return ()++ IndexShaSum opts -> IndexShaSum.run opts+++ AddBound AddBoundOptions {..} -> forM_ optABFiles $ \fp -> do+ old <- BS.readFile fp++ -- idea is simple:+ -- - .cabal is line oriented file+ -- - find "library" section start+ -- - bonus: look of an indentation used from the next field/section there+ -- - insert data into a bytestring "manually"+ fs <- either (exitFailureWith . show) return $ C.readFields old+ (lin, indent) <- maybe+ (exitFailureWith $ "Cannot find library section in " ++ fp)+ return+ (findLibrarySection fs)++ let msgLines = map ("-- " ++) optABMessage+ bdLine = "build-depends: " ++ C.prettyShow optABPackageName ++ " " ++ C.prettyShow optABVersionRange+ midLines = [ BS8.pack $ replicate indent ' ' ++ l+ | l <- msgLines ++ [bdLine]+ ] ++ [""] -- also add an empty line separator+ (preLines, postLines) = splitAt lin $ BS8.lines old+ new = BS8.unlines (preLines ++ midLines ++ postLines)++ -- interpretation of version ranges+ let oldGpd = parseGenericPackageDescription' old+ newGpd = parseGenericPackageDescription' new++ oldRange = extractRange oldGpd optABPackageName+ newRange = extractRange newGpd optABPackageName++ oldRange' = C.intersectVersionRanges oldRange optABVersionRange++ -- Canonical forms (semantics)+ oldSem = C.toVersionIntervals oldRange -- existing range+ oldSem' = C.toVersionIntervals oldRange' -- range after adding the bound (theory)+ newSem = C.toVersionIntervals newRange -- range after adding the bound (practice)++ -- Necessity check: does the addition of the bound change the semantics?+ -- if not, it can be skipped.++ if not optForce && oldSem' == oldSem then do++ putStrLn $ concat [ "Skipping ", fp, ": bound already subsumed by existing constraints (use --force to add nevertheless)." ]++ else do+ -- sanity check: did the addition have the intended outcome?+ unless (newSem == oldSem') $+ exitFailureWith $ unwords+ [ "Edit failed, version ranges don't match: "+ , C.prettyShow oldRange+ , "&&"+ , C.prettyShow optABVersionRange+ , "=/="+ , C.prettyShow newRange+ ]++ -- write new version+ putStrLn $ unwords [ "Adding bound to", fp ]+ BS.writeFile fp new++ return ()+ where+ mkHConn = do+ sslCtx <- baselineContextSSL+ pure $ HConn (openConnectionSSL sslCtx optHost 443) Nothing 0 0++ runHConn act = do+ hc0 <- mkHConn+ flip evalStateT hc0 $ do+ res <- act+ closeHConn+ return res++ getNetrcContents :: IO (Maybe ByteString)+ getNetrcContents = do+ mhome <- lookupEnv "HOME"+ case mhome of+ Nothing -> return Nothing+ Just "" -> return Nothing+ Just ho -> do+ let fnGpg = ho ++ "/.netrc.gpg"+ let fn = ho ++ "/.netrc"+ gpgExists <- doesFileExist fnGpg+ if gpgExists+ then readGpg fnGpg+ else readPlain fn+ where+ readGpg fn = do+ (ec, out, err) <- readProcessWithExitCode "gpg" ["--decrypt", fn] ""+ case ec of+ ExitSuccess -> return (Just out)+ ExitFailure _ -> BS.putStr err >> return Nothing++ readPlain fn = do+ ret <- tryIOError (BS.readFile fn)+ case ret of+ Left e | isDoesNotExistError e -> return Nothing+ | otherwise -> ioError e+ Right b -> return $! Just b++ getHackageCreds :: IO (Maybe (ByteString,ByteString))+ getHackageCreds = do+ getNetrcContents >>= \case+ Nothing -> pure Nothing+ Just contents -> case parseNetRc "netrc" contents of+ Left _ -> fail "Invalid ${HOME}/.netrc(.gpg) found"+ Right NetRc {..} ->+ evaluate $ (\NetRcHost{..} -> (nrhLogin,nrhPassword))+ <$> listToMaybe (filter ((== optHost) . nrhName) nrHosts)+++ pkgDescToPkgIdXrev pdesc = force (BS8.pack pkgn, BS8.pack $ showVersion pkgv, read xrev :: PkgRev)+ where+ C.PackageIdentifier (C.unPackageName -> pkgn) pkgv = C.package . C.packageDescription $ pdesc+ xrev = fromMaybe "0" . lookup "x-revision" . C.customFieldsPD . C.packageDescription $ pdesc++ incrXrev :: ByteString -> ByteString+ incrXrev cabdata0 = cabalEditXRev (xrev0+1) cabdata0+ where+ pdesc0 = parseGenericPackageDescription' cabdata0+ (_,_,xrev0) = pkgDescToPkgIdXrev pdesc0++ exitFailureWith e = do+ putStrLn e+ exitFailure++ parseGenericPackageDescription' bs =+ case snd $ C.runParseResult $ C.parseGenericPackageDescription bs of+ Left (_, es) -> error $ List.intercalate "\n" $ map (C.showPError "<.cabal>") $ toList es+ Right x -> x++ extractRange gpd pkgName = case vss of+ [] -> C.noVersion+ (v:vs) -> List.foldl' C.intersectVersionRanges v vs+ where+ vss = gpd ^.. LC.condLibrary . _Just . condTreeDataL . LC.targetBuildDepends . traverse . to ext . _Just+ ext (C.Dependency pkgName' vr _)+ | pkgName == pkgName' = Just vr+ | otherwise = Nothing++ condTreeDataL :: Functor f => (a -> f a) -> C.CondTree v c a -> f (C.CondTree v c a)+ condTreeDataL f (C.CondNode x c cs) = f x <&> \y -> C.CondNode y c cs+++-- | Try to clean-up HTML fragments to be more readable+tidyHtml :: ByteString -> ByteString+tidyHtml =+ replace "&" "&" . -- must be last entity substitution+ replace ">" ">" .+ replace "<" "<" .+ replace "<p>" "" . -- tags must be replaced before entities+ replace "</p>" "\n" .+ replace "<li>" "\n * " .+ replace "</li>" "" .+ replace "</pre>" "`" .+ replace "<pre>" "`" .+ stripEnd+ where+ stripEnd = fst . BS8.spanEnd isSpace++ replace :: ByteString -> ByteString -> ByteString -> ByteString+ replace old new = BSL.toStrict . BSS.replace old new+++timeIt :: IO a -> IO (Double, a)+timeIt act = do+ t0 <- getTime+ res <- act+ t1 <- getTime+ let !dt = t1 - t0+ pure (dt, res)+ where+ getTime :: IO Double+ getTime = realToFrac `fmap` getPOSIXTime
+ tests/Tests.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE TupleSections #-}+module Main (main) where+++import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import Distribution.Server.Util.CabalRevisions (diffCabalRevisions', Change (..))+import Distribution.Simple.Utils (toUTF8BS)+import System.FilePath ((</>), (-<.>))+import Test.Tasty (defaultMain, testGroup, TestTree)+import Test.Tasty.Golden (goldenVsStringDiff)++main :: IO ()+main = defaultMain $ testGroup "Fixtures"+ -- basic sanity tests+ [ golden "tree-diff" 0 1+ , golden "deepseq" 0 1++ -- adding a new conditional section with restricted bounds+ -- TODO: this is not allowed+ , golden "SVGFonts" 0 2++ , golden "semigroups" 0 2+ ]++golden :: String -> Int -> Int -> TestTree+golden name mi ma = case pairs [mi .. ma] of+ [] -> golden' name mi ma+ [(x, y)] -> golden' name x y+ ps -> testGroup name+ [ golden' name x y+ | (x, y) <- ps+ ]++golden' :: String -> Int -> Int -> TestTree+golden' name mi ma = goldenVsStringDiff name' diff gold $ do+ o <- BS.readFile orig+ e <- BS.readFile edit+ return $ LBS.fromStrict $ toUTF8BS $ unlines $ case diffCabalRevisions' False o e of+ Left err -> [ "ERROR", err ]+ Right changes -> "OK" : concatMap showChange changes+ where+ name' = unwords [ name, show mi, "->", show ma ]+ orig = "fixtures" </> name -<.> (show mi ++ ".cabal")+ edit = "fixtures" </> name -<.> (show ma ++ ".cabal")+ gold = "fixtures" </> name -<.> (show mi ++ "." ++ show ma ++ ".diff")++ diff ref new = ["diff", "-u", ref, new]++ showChange (Change sev what from to) =+ [ show sev+ , what+ , "- " ++ from+ , "+ " ++ to+ ]++-- cartesian product without diagonal+-- the idea is that revisions should be transitive and symmetric+pairs :: [a] -> [(a,a)]+pairs [] = []+pairs (x:xs) = map (x,) xs ++ map (,x) xs ++ pairs xs