mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-21 04:02:53 +02:00
merge main
This commit is contained in:
commit
335375fd76
211 files changed
+3789
-3048
No files matched your search
@@ -9,6 +9,7 @@ _test
|
||||
|
||||
# IntelliJ
|
||||
.idea
|
||||
.run
|
||||
|
||||
# IntelliJ Gateway
|
||||
.uuid
|
||||
|
||||
Generated
+16
-6
@@ -874,6 +874,11 @@
|
||||
"path": "github.com/mitchellh/mapstructure/LICENSE",
|
||||
"licenseText": "The MIT License (MIT)\n\nCopyright (c) 2013 Mitchell Hashimoto\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/mmcloughlin/avo",
|
||||
"path": "github.com/mmcloughlin/avo/LICENSE",
|
||||
"licenseText": "BSD 3-Clause License\n\nCopyright (c) 2018, Michael McLoughlin\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n this list of conditions and the following disclaimer in the documentation\n and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n contributors may be used to endorse or promote products derived from\n this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/modern-go/concurrent",
|
||||
"path": "github.com/modern-go/concurrent/LICENSE",
|
||||
@@ -937,7 +942,7 @@
|
||||
{
|
||||
"name": "github.com/pjbgf/sha1cd",
|
||||
"path": "github.com/pjbgf/sha1cd/LICENSE",
|
||||
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under thiLine truncated
|
||||
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under thiLine truncated
|
||||
},
|
||||
{
|
||||
"name": "github.com/pkg/errors",
|
||||
@@ -1089,11 +1094,6 @@
|
||||
"path": "github.com/x448/float16/LICENSE",
|
||||
"licenseText": "MIT License\n\nCopyright (c) 2019 Montgomery Edwards⁴⁴⁸ and Faye Amacker\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
|
||||
},
|
||||
{
|
||||
"name": "github.com/xanzy/go-gitlab",
|
||||
"path": "github.com/xanzy/go-gitlab/LICENSE",
|
||||
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under thiLine truncated
|
||||
},
|
||||
{
|
||||
"name": "github.com/xanzy/ssh-agent",
|
||||
"path": "github.com/xanzy/ssh-agent/LICENSE",
|
||||
@@ -1129,6 +1129,11 @@
|
||||
"path": "github.com/zeebo/blake3/LICENSE",
|
||||
"licenseText": "This work is released into the public domain with CC0 1.0.\n\n-------------------------------------------------------------------------------\n\nCreative Commons Legal Code\n\nCC0 1.0 Universal\n\n CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n i. the right to reproduce, adapt, distribute, perform, display,\n communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n subject to the limitations in paragraph 4(a), below;\n v. rights protecting the extraction, dissemination, use and reuse of data\n in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n European Parliament and of the Council of 11 March 1996 on the legal\n protection of databases, and under any national implementation\n thereof, including any amended or successor version of such\n directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n world based on applicable law or treaty, and any national\n implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affecLine truncated
|
||||
},
|
||||
{
|
||||
"name": "gitlab.com/gitlab-org/api/client-go",
|
||||
"path": "gitlab.com/gitlab-org/api/client-go/LICENSE",
|
||||
"licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under thiLine truncated
|
||||
},
|
||||
{
|
||||
"name": "go.etcd.io/bbolt",
|
||||
"path": "go.etcd.io/bbolt/LICENSE",
|
||||
@@ -1194,6 +1199,11 @@
|
||||
"path": "golang.org/x/time/rate/LICENSE",
|
||||
"licenseText": "Copyright 2009 The Go Authors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google LLC nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "golang.org/x/tools",
|
||||
"path": "golang.org/x/tools/LICENSE",
|
||||
"licenseText": "Copyright 2009 The Go Authors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n * Redistributions of source code must retain the above copyright\nnotice, this list of conditions and the following disclaimer.\n * Redistributions in binary form must reproduce the above\ncopyright notice, this list of conditions and the following disclaimer\nin the documentation and/or other materials provided with the\ndistribution.\n * Neither the name of Google LLC nor the names of its\ncontributors may be used to endorse or promote products derived from\nthis software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS\n\"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT\nOWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\nSPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\nLIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\nDATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\nTHEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
|
||||
},
|
||||
{
|
||||
"name": "google.golang.org/genproto/googleapis/rpc/status",
|
||||
"path": "google.golang.org/genproto/googleapis/rpc/status/LICENSE",
|
||||
|
||||
+4
-2
@@ -54,8 +54,10 @@ func runACME(listenAddr string, m http.Handler) error {
|
||||
altTLSALPNPort = p
|
||||
}
|
||||
|
||||
magic := &certmagic.Default
|
||||
magic.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||
// FIXME: this path is not right, it uses "AppWorkPath" incorrectly, and writes the data into "AppWorkPath/https"
|
||||
// Ideally it should migrate to AppDataPath write to "AppDataPath/https"
|
||||
certmagic.Default.Storage = &certmagic.FileStorage{Path: setting.AcmeLiveDirectory}
|
||||
magic := certmagic.NewDefault()
|
||||
// Try to use private CA root if provided, otherwise defaults to system's trust
|
||||
var certPool *x509.CertPool
|
||||
if setting.AcmeCARoot != "" {
|
||||
|
||||
+31
-24
@@ -78,8 +78,9 @@ RUN_USER = ; git
|
||||
;; Set the domain for the server
|
||||
;DOMAIN = localhost
|
||||
;;
|
||||
;; Overwrite the automatically generated public URL. Necessary for proxies and docker.
|
||||
;ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
|
||||
;; The AppURL used by Gitea to generate absolute links, defaults to "{PROTOCOL}://{DOMAIN}:{HTTP_PORT}/".
|
||||
;; Most users should set it to the real website URL of their Gitea instance.
|
||||
;ROOT_URL =
|
||||
;;
|
||||
;; For development purpose only. It makes Gitea handle sub-path ("/sub-path/owner/repo/...") directly when debugging without a reverse proxy.
|
||||
;; DO NOT USE IT IN PRODUCTION!!!
|
||||
@@ -103,8 +104,8 @@ RUN_USER = ; git
|
||||
;REDIRECT_OTHER_PORT = false
|
||||
;PORT_TO_REDIRECT = 80
|
||||
;;
|
||||
;; expect PROXY protocol header on connections to https redirector.
|
||||
;REDIRECTOR_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
|
||||
;; expect PROXY protocol header on connections to https redirector, defaults to USE_PROXY_PROTOCOL
|
||||
;REDIRECTOR_USE_PROXY_PROTOCOL =
|
||||
;; Minimum and maximum supported TLS versions
|
||||
;SSL_MIN_VERSION=TLSv1.2
|
||||
;SSL_MAX_VERSION=
|
||||
@@ -128,13 +129,14 @@ RUN_USER = ; git
|
||||
;; most cases you do not need to change the default value. Alter it only if
|
||||
;; your SSH server node is not the same as HTTP node. For different protocol, the default
|
||||
;; values are different. If `PROTOCOL` is `http+unix`, the default value is `http://unix/`.
|
||||
;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
|
||||
;; If listen on `0.0.0.0`, the default value is `%(PROTOCOL)s://localhost:%(HTTP_PORT)s/`, Otherwise the default
|
||||
;; value is `%(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/`.
|
||||
;LOCAL_ROOT_URL = %(PROTOCOL)s://%(HTTP_ADDR)s:%(HTTP_PORT)s/
|
||||
;; If `PROTOCOL` is `fcgi` or `fcgi+unix`, the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
|
||||
;; If listen on `0.0.0.0`, the default value is `{PROTOCOL}://localhost:{HTTP_PORT}/`.
|
||||
;; Otherwise the default value is `{PROTOCOL}://{HTTP_ADDR}:{HTTP_PORT}/`.
|
||||
;; Most users don't need (and shouldn't) set this value.
|
||||
;LOCAL_ROOT_URL =
|
||||
;;
|
||||
;; When making local connections pass the PROXY protocol header.
|
||||
;LOCAL_USE_PROXY_PROTOCOL = %(USE_PROXY_PROTOCOL)s
|
||||
;; When making local connections pass the PROXY protocol header, defaults to USE_PROXY_PROTOCOL
|
||||
;LOCAL_USE_PROXY_PROTOCOL =
|
||||
;;
|
||||
;; Disable SSH feature when not available
|
||||
;DISABLE_SSH = false
|
||||
@@ -146,13 +148,17 @@ RUN_USER = ; git
|
||||
;SSH_SERVER_USE_PROXY_PROTOCOL = false
|
||||
;;
|
||||
;; Username to use for the builtin SSH server. If blank, then it is the value of RUN_USER.
|
||||
;BUILTIN_SSH_SERVER_USER = %(RUN_USER)s
|
||||
;BUILTIN_SSH_SERVER_USER =
|
||||
;;
|
||||
;; Domain name to be exposed in clone URL
|
||||
;SSH_DOMAIN = %(DOMAIN)s
|
||||
;; Domain name to be exposed in clone URL, defaults to DOMAIN or the domain part of ROOT_URL
|
||||
;SSH_DOMAIN =
|
||||
;;
|
||||
;; SSH username displayed in clone URLs.
|
||||
;SSH_USER = %(BUILTIN_SSH_SERVER_USER)s
|
||||
;; SSH username displayed in clone URLs. It defaults to BUILTIN_SSH_SERVER_USER or RUN_USER.
|
||||
;; If it is set to "(DOER_USERNAME)", it will use current signed-in user's username.
|
||||
;; This option is only for some advanced users who have configured their SSH reverse-proxy
|
||||
;; and need to use different usernames for git SSH clone.
|
||||
;; Most users should just leave it blank.
|
||||
;SSH_USER =
|
||||
;;
|
||||
;; The network interface the builtin SSH server should listen on
|
||||
;SSH_LISTEN_HOST =
|
||||
@@ -160,8 +166,8 @@ RUN_USER = ; git
|
||||
;; Port number to be exposed in clone URL
|
||||
;SSH_PORT = 22
|
||||
;;
|
||||
;; The port number the builtin SSH server should listen on
|
||||
;SSH_LISTEN_PORT = %(SSH_PORT)s
|
||||
;; The port number the builtin SSH server should listen on, defaults to SSH_PORT
|
||||
;SSH_LISTEN_PORT =
|
||||
;;
|
||||
;; Root path of SSH directory, default is '~/.ssh', but you have to use '/home/git/.ssh'.
|
||||
;SSH_ROOT_PATH =
|
||||
@@ -188,7 +194,7 @@ RUN_USER = ; git
|
||||
;;
|
||||
;; For the built-in SSH server, choose the keypair to offer as the host key
|
||||
;; The private key should be at SSH_SERVER_HOST_KEY and the public SSH_SERVER_HOST_KEY.pub
|
||||
;; relative paths are made absolute relative to the %(APP_DATA_PATH)s
|
||||
;; relative paths are made absolute relative to the APP_DATA_PATH
|
||||
;SSH_SERVER_HOST_KEYS=ssh/gitea.rsa, ssh/gogs.rsa
|
||||
;;
|
||||
;; Directory to create temporary files in when testing public keys using ssh-keygen,
|
||||
@@ -582,7 +588,7 @@ ENABLED = true
|
||||
[log]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Root path for the log files - defaults to %(GITEA_WORK_DIR)/log
|
||||
;; Root path for the log files - defaults to "{AppWorkPath}/log"
|
||||
;ROOT_PATH =
|
||||
;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -682,8 +688,8 @@ LEVEL = Info
|
||||
;; The path of git executable. If empty, Gitea searches through the PATH environment.
|
||||
;PATH =
|
||||
;;
|
||||
;; The HOME directory for Git
|
||||
;HOME_PATH = %(APP_DATA_PATH)s/home
|
||||
;; The HOME directory for Git, defaults to "{APP_DATA_PATH}/home"
|
||||
;HOME_PATH =
|
||||
;;
|
||||
;; Disables highlight of added and removed changes
|
||||
;DISABLE_DIFF_HIGHLIGHT = false
|
||||
@@ -946,8 +952,8 @@ LEVEL = Info
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;[repository]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Root path for storing all repository data. By default, it is set to %(APP_DATA_PATH)s/gitea-repositories.
|
||||
;; A relative path is interpreted as _`AppWorkPath`_/%(ROOT)s
|
||||
;; Root path for storing all repository data. By default, it is set to "{APP_DATA_PATH}/gitea-repositories".
|
||||
;; A relative path is interpreted as "{AppWorkPath}/{ROOT}" (use AppWorkPath as base path).
|
||||
;ROOT =
|
||||
;;
|
||||
;; The script type this server supports. Usually this is `bash`, but some users report that only `sh` is available.
|
||||
@@ -1506,7 +1512,8 @@ LEVEL = Info
|
||||
;TYPE = persistable-channel
|
||||
;;
|
||||
;; data-dir for storing persistable queues and level queues, individual queues will default to `queues/common` meaning the queue is shared.
|
||||
;DATADIR = queues/ ; Relative paths will be made absolute against `%(APP_DATA_PATH)s`.
|
||||
;; Relative paths will be made absolute against "APP_DATA_PATH"
|
||||
;DATADIR = queues/
|
||||
;;
|
||||
;; Default queue length before a channel queue will block
|
||||
;LENGTH = 100000
|
||||
|
||||
@@ -24,10 +24,10 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
|
||||
github.com/ProtonMail/go-crypto v1.0.0
|
||||
github.com/ProtonMail/go-crypto v1.1.4
|
||||
github.com/PuerkitoBio/goquery v1.10.0
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
|
||||
github.com/alecthomas/chroma/v2 v2.14.0
|
||||
github.com/alecthomas/chroma/v2 v2.15.0
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.42
|
||||
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
@@ -54,8 +54,8 @@ require (
|
||||
github.com/go-chi/cors v1.2.1
|
||||
github.com/go-co-op/gocron v1.37.0
|
||||
github.com/go-enry/go-enry/v2 v2.9.1
|
||||
github.com/go-git/go-billy/v5 v5.6.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-git/go-billy/v5 v5.6.1
|
||||
github.com/go-git/go-git/v5 v5.13.1
|
||||
github.com/go-ldap/ldap/v3 v3.4.8
|
||||
github.com/go-redsync/redsync/v4 v4.13.0
|
||||
github.com/go-sql-driver/mysql v1.8.1
|
||||
@@ -71,7 +71,6 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/h2non/gock v1.2.0
|
||||
github.com/hashicorp/go-version v1.7.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/huandu/xstrings v1.5.0
|
||||
@@ -107,28 +106,28 @@ require (
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/shurcooL/vfsgen v0.0.0-20230704071429-0000e147ea92
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.12
|
||||
github.com/urfave/cli/v2 v2.27.5
|
||||
github.com/wneessen/go-mail v0.5.2
|
||||
github.com/xanzy/go-gitlab v0.112.0
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/yohcop/openid-go v1.0.1
|
||||
github.com/yuin/goldmark v1.7.8
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.119.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/image v0.21.0
|
||||
golang.org/x/net v0.33.0
|
||||
golang.org/x/oauth2 v0.23.0
|
||||
golang.org/x/net v0.34.0
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/sys v0.28.0
|
||||
golang.org/x/sys v0.29.0
|
||||
golang.org/x/text v0.21.0
|
||||
golang.org/x/tools v0.26.0
|
||||
golang.org/x/tools v0.29.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
google.golang.org/protobuf v1.35.1
|
||||
google.golang.org/protobuf v1.36.0
|
||||
gopkg.in/ini.v1 v1.67.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
mvdan.cc/xurls/v2 v2.5.0
|
||||
@@ -187,7 +186,7 @@ require (
|
||||
github.com/couchbase/gomemcached v0.3.2 // indirect
|
||||
github.com/couchbase/goutils v0.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
@@ -220,7 +219,7 @@ require (
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
@@ -230,7 +229,6 @@ require (
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
@@ -255,6 +253,7 @@ require (
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/mmcloughlin/avo v0.6.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
@@ -266,7 +265,7 @@ require (
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.60.1 // indirect
|
||||
@@ -306,8 +305,8 @@ require (
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/time v0.7.0 // indirect
|
||||
golang.org/x/mod v0.22.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
|
||||
@@ -71,8 +71,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.1.4 h1:G5U5asvD5N/6/36oIw3k2bOfBn5XVcZrb7PBjzzKKoE=
|
||||
github.com/ProtonMail/go-crypto v1.1.4/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
|
||||
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
|
||||
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
|
||||
@@ -81,11 +81,11 @@ github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv
|
||||
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
|
||||
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
|
||||
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
||||
github.com/alecthomas/chroma/v2 v2.15.0 h1:LxXTQHFoYrstG2nnV9y2X5O94sOBzf0CIUpSTbpxvMc=
|
||||
github.com/alecthomas/chroma/v2 v2.15.0/go.mod h1:gUhVLrPDXPtp/f+L1jo9xepo9gL4eLwRuGAunSZMkio=
|
||||
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
@@ -188,7 +188,6 @@ github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk=
|
||||
github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
|
||||
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
|
||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
||||
@@ -205,7 +204,6 @@ github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moA
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
|
||||
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
@@ -223,8 +221,8 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
|
||||
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
@@ -254,8 +252,8 @@ github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJ
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2 h1:dKG8sc7n321deIVRcQtwlMNoBEra7j0qQ8RwxO8RN0w=
|
||||
github.com/editorconfig/editorconfig-core-go/v2 v2.6.2/go.mod h1:7dvD3GCm7eBw53xZ/lsiq72LqobdMg3ITbMBxnmJmqY=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
|
||||
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
|
||||
github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjTA=
|
||||
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
|
||||
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
|
||||
@@ -313,12 +311,12 @@ github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5Hql
|
||||
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
|
||||
github.com/go-git/go-billy/v5 v5.6.1 h1:u+dcrgaguSSkbjzHwelEjc0Yj300NUevrrPphk/SoRA=
|
||||
github.com/go-git/go-billy/v5 v5.6.1/go.mod h1:0AsLr1z2+Uksi4NlElmMblP5rPcDZNRCD8ujZCRR2BE=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
|
||||
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
|
||||
@@ -385,8 +383,8 @@ github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei
|
||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 h1:HKlyj6in2JV6wVkmQ4XmG/EIm+SCYlPZ+V4GWit7Z+I=
|
||||
github.com/golang/geo v0.0.0-20230421003525-6adc56603217/go.mod h1:8wI0hitZ3a1IxZfeH3/5I97CI8i5cLGsYe7xNhQGs9U=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
@@ -452,10 +450,6 @@ github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -585,6 +579,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/mmcloughlin/avo v0.6.0 h1:QH6FU8SKoTLaVs80GA8TJuLNkUYl4VokHKlPhVDg4YY=
|
||||
github.com/mmcloughlin/avo v0.6.0/go.mod h1:8CoAGaCSYXtCPR+8y18Y9aB/kxb8JSS6FRI7mSkvD+8=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -599,8 +595,6 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
|
||||
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
|
||||
github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
|
||||
github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek=
|
||||
github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
@@ -637,8 +631,8 @@ github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pjbgf/sha1cd v0.3.1 h1:Dh2GYdpJnO84lIw0LJwTFXjcNbasP/bklicSznyAaPI=
|
||||
github.com/pjbgf/sha1cd v0.3.1/go.mod h1:Y8t7jSB/dEI/lQE04A1HVKteqjj9bX5O4+Cex0TCu8s=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -743,8 +737,8 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
|
||||
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203/go.mod h1:oqN97ltKNihBbwlX8dLpwxCl3+HnXKV/R0e+sRLd9C8=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
@@ -772,8 +766,6 @@ github.com/wneessen/go-mail v0.5.2 h1:MZKwgHJoRboLJ+EHMLuHpZc95wo+u1xViL/4XSswDT
|
||||
github.com/wneessen/go-mail v0.5.2/go.mod h1:kRroJvEq2hOSEPFRiKjN7Csrz0G1w+RpiGR3b6yo+Ck=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
|
||||
github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -808,6 +800,8 @@ github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
gitlab.com/gitlab-org/api/client-go v0.119.0 h1:YBZyx9XUTtEDBBYtY36cZWz6JmT7om/8HPSk37IS95g=
|
||||
gitlab.com/gitlab-org/api/client-go v0.119.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ=
|
||||
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||
@@ -829,16 +823,14 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||
@@ -852,8 +844,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -865,20 +857,18 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -916,8 +906,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -927,14 +915,12 @@ golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
@@ -942,15 +928,13 @@ golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
@@ -958,8 +942,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
@@ -970,8 +954,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -986,8 +970,8 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ=
|
||||
google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -154,7 +154,7 @@ func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
||||
}
|
||||
|
||||
func (run *ActionRun) GetPullRequestEventPayload() (*api.PullRequestPayload, error) {
|
||||
if run.Event == webhook_module.HookEventPullRequest || run.Event == webhook_module.HookEventPullRequestSync {
|
||||
if run.Event.IsPullRequest() {
|
||||
var payload api.PullRequestPayload
|
||||
if err := json.Unmarshal([]byte(run.EventPayload), &payload); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/keybase/go-crypto/openpgp/packet"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCheckArmoredGPGKeyString(t *testing.T) {
|
||||
@@ -107,9 +108,8 @@ MkM/fdpyc2hY7Dl/+qFmN5MG5yGmMpQcX+RNNR222ibNC1D3wg==
|
||||
=i9b7
|
||||
-----END PGP PUBLIC KEY BLOCK-----`
|
||||
keys, err := checkArmoredGPGKeyString(testGPGArmor)
|
||||
if !assert.NotEmpty(t, keys) {
|
||||
return
|
||||
}
|
||||
require.NotEmpty(t, keys)
|
||||
|
||||
ekey := keys[0]
|
||||
assert.NoError(t, err, "Could not parse a valid GPG armored key", ekey)
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDumpDatabase(t *testing.T) {
|
||||
@@ -62,9 +63,7 @@ func TestPrimaryKeys(t *testing.T) {
|
||||
// Import "code.gitea.io/gitea/cmd" to make sure each db.RegisterModel in init functions has been called.
|
||||
|
||||
beans, err := db.NamesToBean()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
whitelist := map[string]string{
|
||||
"the_table_name_to_skip_checking": "Write a note here to explain why",
|
||||
@@ -79,8 +78,6 @@ func TestPrimaryKeys(t *testing.T) {
|
||||
t.Logf("ignore %q because %q", table.Name, why)
|
||||
continue
|
||||
}
|
||||
if len(table.PrimaryKeys) == 0 {
|
||||
t.Errorf("table %q has no primary key", table.Name)
|
||||
}
|
||||
assert.NotEmpty(t, table.PrimaryKeys, "table %q has no primary key", table.Name)
|
||||
}
|
||||
}
|
||||
@@ -46,23 +46,6 @@ func (err ErrIssueNotExist) Unwrap() error {
|
||||
return util.ErrNotExist
|
||||
}
|
||||
|
||||
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
|
||||
type ErrIssueIsClosed struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
Index int64
|
||||
}
|
||||
|
||||
// IsErrIssueIsClosed checks if an error is a ErrIssueNotExist.
|
||||
func IsErrIssueIsClosed(err error) bool {
|
||||
_, ok := err.(ErrIssueIsClosed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrIssueIsClosed) Error() string {
|
||||
return fmt.Sprintf("issue is closed [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
|
||||
}
|
||||
|
||||
// ErrNewIssueInsert is used when the INSERT statement in newIssue fails
|
||||
type ErrNewIssueInsert struct {
|
||||
OriginalError error
|
||||
@@ -78,22 +61,6 @@ func (err ErrNewIssueInsert) Error() string {
|
||||
return err.OriginalError.Error()
|
||||
}
|
||||
|
||||
// ErrIssueWasClosed is used when close a closed issue
|
||||
type ErrIssueWasClosed struct {
|
||||
ID int64
|
||||
Index int64
|
||||
}
|
||||
|
||||
// IsErrIssueWasClosed checks if an error is a ErrIssueWasClosed.
|
||||
func IsErrIssueWasClosed(err error) bool {
|
||||
_, ok := err.(ErrIssueWasClosed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrIssueWasClosed) Error() string {
|
||||
return fmt.Sprintf("Issue [%d] %d was already closed", err.ID, err.Index)
|
||||
}
|
||||
|
||||
var ErrIssueAlreadyChanged = util.NewInvalidArgumentErrorf("the issue is already changed")
|
||||
|
||||
// Issue represents an issue or pull request of repository.
|
||||
|
||||
@@ -28,38 +28,40 @@ import (
|
||||
|
||||
// UpdateIssueCols updates cols of issue
|
||||
func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
|
||||
if _, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err := db.GetEngine(ctx).ID(issue.ID).Cols(cols...).Update(issue)
|
||||
return err
|
||||
}
|
||||
|
||||
func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
|
||||
// Reload the issue
|
||||
currentIssue, err := GetIssueByID(ctx, issue.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ErrIssueIsClosed is used when close a closed issue
|
||||
type ErrIssueIsClosed struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
Index int64
|
||||
IsPull bool
|
||||
}
|
||||
|
||||
// Nothing should be performed if current status is same as target status
|
||||
if currentIssue.IsClosed == isClosed {
|
||||
if !issue.IsPull {
|
||||
return nil, ErrIssueWasClosed{
|
||||
ID: issue.ID,
|
||||
}
|
||||
}
|
||||
return nil, ErrPullWasClosed{
|
||||
ID: issue.ID,
|
||||
// IsErrIssueIsClosed checks if an error is a ErrIssueIsClosed.
|
||||
func IsErrIssueIsClosed(err error) bool {
|
||||
_, ok := err.(ErrIssueIsClosed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrIssueIsClosed) Error() string {
|
||||
return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already closed", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
|
||||
}
|
||||
|
||||
func SetIssueAsClosed(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
|
||||
if issue.IsClosed {
|
||||
return nil, ErrIssueIsClosed{
|
||||
ID: issue.ID,
|
||||
RepoID: issue.RepoID,
|
||||
Index: issue.Index,
|
||||
IsPull: issue.IsPull,
|
||||
}
|
||||
}
|
||||
|
||||
issue.IsClosed = isClosed
|
||||
return doChangeIssueStatus(ctx, issue, doer, isMergePull)
|
||||
}
|
||||
|
||||
func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) {
|
||||
// Check for open dependencies
|
||||
if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) {
|
||||
if issue.Repo.IsDependenciesEnabled(ctx) {
|
||||
// only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies
|
||||
noDeps, err := IssueNoDependenciesLeft(ctx, issue)
|
||||
if err != nil {
|
||||
@@ -71,16 +73,63 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
|
||||
}
|
||||
}
|
||||
|
||||
if issue.IsClosed {
|
||||
issue.ClosedUnix = timeutil.TimeStampNow()
|
||||
} else {
|
||||
issue.ClosedUnix = 0
|
||||
}
|
||||
issue.IsClosed = true
|
||||
issue.ClosedUnix = timeutil.TimeStampNow()
|
||||
|
||||
if err := UpdateIssueCols(ctx, issue, "is_closed", "closed_unix"); err != nil {
|
||||
if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
|
||||
Where("is_closed = ?", false).
|
||||
Update(issue); err != nil {
|
||||
return nil, err
|
||||
} else if cnt != 1 {
|
||||
return nil, ErrIssueAlreadyChanged
|
||||
}
|
||||
|
||||
return updateIssueNumbers(ctx, issue, doer, util.Iif(isMergePull, CommentTypeMergePull, CommentTypeClose))
|
||||
}
|
||||
|
||||
// ErrIssueIsOpen is used when reopen an opened issue
|
||||
type ErrIssueIsOpen struct {
|
||||
ID int64
|
||||
RepoID int64
|
||||
IsPull bool
|
||||
Index int64
|
||||
}
|
||||
|
||||
// IsErrIssueIsOpen checks if an error is a ErrIssueIsOpen.
|
||||
func IsErrIssueIsOpen(err error) bool {
|
||||
_, ok := err.(ErrIssueIsOpen)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrIssueIsOpen) Error() string {
|
||||
return fmt.Sprintf("%s [id: %d, repo_id: %d, index: %d] is already open", util.Iif(err.IsPull, "Pull Request", "Issue"), err.ID, err.RepoID, err.Index)
|
||||
}
|
||||
|
||||
func setIssueAsReopen(ctx context.Context, issue *Issue, doer *user_model.User) (*Comment, error) {
|
||||
if !issue.IsClosed {
|
||||
return nil, ErrIssueIsOpen{
|
||||
ID: issue.ID,
|
||||
RepoID: issue.RepoID,
|
||||
Index: issue.Index,
|
||||
IsPull: issue.IsPull,
|
||||
}
|
||||
}
|
||||
|
||||
issue.IsClosed = false
|
||||
issue.ClosedUnix = 0
|
||||
|
||||
if cnt, err := db.GetEngine(ctx).ID(issue.ID).Cols("is_closed", "closed_unix").
|
||||
Where("is_closed = ?", true).
|
||||
Update(issue); err != nil {
|
||||
return nil, err
|
||||
} else if cnt != 1 {
|
||||
return nil, ErrIssueAlreadyChanged
|
||||
}
|
||||
|
||||
return updateIssueNumbers(ctx, issue, doer, CommentTypeReopen)
|
||||
}
|
||||
|
||||
func updateIssueNumbers(ctx context.Context, issue *Issue, doer *user_model.User, cmtType CommentType) (*Comment, error) {
|
||||
// Update issue count of labels
|
||||
if err := issue.LoadLabels(ctx); err != nil {
|
||||
return nil, err
|
||||
@@ -103,14 +152,6 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// New action comment
|
||||
cmtType := CommentTypeClose
|
||||
if !issue.IsClosed {
|
||||
cmtType = CommentTypeReopen
|
||||
} else if isMergePull {
|
||||
cmtType = CommentTypeMergePull
|
||||
}
|
||||
|
||||
return CreateComment(ctx, &CreateCommentOptions{
|
||||
Type: cmtType,
|
||||
Doer: doer,
|
||||
@@ -134,7 +175,7 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
comment, err := ChangeIssueStatus(ctx, issue, doer, true, false)
|
||||
comment, err := SetIssueAsClosed(ctx, issue, doer, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -159,7 +200,7 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
|
||||
}
|
||||
defer committer.Close()
|
||||
|
||||
comment, err := ChangeIssueStatus(ctx, issue, doer, false, false)
|
||||
comment, err := setIssueAsReopen(ctx, issue, doer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+2
-18
@@ -80,22 +80,6 @@ func (err ErrPullRequestAlreadyExists) Unwrap() error {
|
||||
return util.ErrAlreadyExist
|
||||
}
|
||||
|
||||
// ErrPullWasClosed is used close a closed pull request
|
||||
type ErrPullWasClosed struct {
|
||||
ID int64
|
||||
Index int64
|
||||
}
|
||||
|
||||
// IsErrPullWasClosed checks if an error is a ErrErrPullWasClosed.
|
||||
func IsErrPullWasClosed(err error) bool {
|
||||
_, ok := err.(ErrPullWasClosed)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrPullWasClosed) Error() string {
|
||||
return fmt.Sprintf("Pull request [%d] %d was already closed", err.ID, err.Index)
|
||||
}
|
||||
|
||||
// PullRequestType defines pull request type
|
||||
type PullRequestType int
|
||||
|
||||
@@ -301,7 +285,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
|
||||
reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -320,7 +304,7 @@ func (pr *PullRequest) LoadRequestedReviewers(ctx context.Context) error {
|
||||
|
||||
// LoadRequestedReviewersTeams loads the requested reviewers teams.
|
||||
func (pr *PullRequest) LoadRequestedReviewersTeams(ctx context.Context) error {
|
||||
reviews, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
|
||||
reviews, _, err := GetReviewsByIssueID(ctx, pr.Issue.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -166,6 +166,23 @@ func (prs PullRequestList) getRepositoryIDs() []int64 {
|
||||
return repoIDs.Values()
|
||||
}
|
||||
|
||||
func (prs PullRequestList) SetBaseRepo(baseRepo *repo_model.Repository) {
|
||||
for _, pr := range prs {
|
||||
if pr.BaseRepo == nil {
|
||||
pr.BaseRepo = baseRepo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (prs PullRequestList) SetHeadRepo(headRepo *repo_model.Repository) {
|
||||
for _, pr := range prs {
|
||||
if pr.HeadRepo == nil {
|
||||
pr.HeadRepo = headRepo
|
||||
pr.isHeadRepoLoaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (prs PullRequestList) LoadRepositories(ctx context.Context) error {
|
||||
repoIDs := prs.getRepositoryIDs()
|
||||
reposMap := make(map[int64]*repo_model.Repository, len(repoIDs))
|
||||
|
||||
@@ -5,6 +5,8 @@ package issues
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
organization_model "code.gitea.io/gitea/models/organization"
|
||||
@@ -153,43 +155,60 @@ func CountReviews(ctx context.Context, opts FindReviewOptions) (int64, error) {
|
||||
return db.GetEngine(ctx).Where(opts.toCond()).Count(&Review{})
|
||||
}
|
||||
|
||||
// GetReviewersFromOriginalAuthorsByIssueID gets the latest review of each original authors for a pull request
|
||||
func GetReviewersFromOriginalAuthorsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
|
||||
reviews := make([]*Review, 0, 10)
|
||||
|
||||
// Get latest review of each reviewer, sorted in order they were made
|
||||
if err := db.GetEngine(ctx).SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND original_author_id <> 0 GROUP BY issue_id, original_author_id) ORDER BY review.updated_unix ASC",
|
||||
issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest).
|
||||
Find(&reviews); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reviews, nil
|
||||
}
|
||||
|
||||
// GetReviewsByIssueID gets the latest review of each reviewer for a pull request
|
||||
func GetReviewsByIssueID(ctx context.Context, issueID int64) (ReviewList, error) {
|
||||
// The first returned parameter is the latest review of each individual reviewer or team
|
||||
// The second returned parameter is the latest review of each original author which is migrated from other systems
|
||||
// The reviews are sorted by updated time
|
||||
func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, migratedOriginalReviews ReviewList, err error) {
|
||||
reviews := make([]*Review, 0, 10)
|
||||
|
||||
sess := db.GetEngine(ctx)
|
||||
|
||||
// Get latest review of each reviewer, sorted in order they were made
|
||||
if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id = 0 AND type in (?, ?, ?) AND dismissed = ? AND original_author_id = 0 GROUP BY issue_id, reviewer_id) ORDER BY review.updated_unix ASC",
|
||||
issueID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, false).
|
||||
Find(&reviews); err != nil {
|
||||
return nil, err
|
||||
// Get all reviews for the issue id
|
||||
if err := db.GetEngine(ctx).Where("issue_id=?", issueID).OrderBy("updated_unix ASC").Find(&reviews); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// filter them in memory to get the latest review of each reviewer
|
||||
// Since the reviews should not be too many for one issue, less than 100 commonly, it's acceptable to do this in memory
|
||||
// And since there are too less indexes in review table, it will be very slow to filter in the database
|
||||
reviewersMap := make(map[int64][]*Review) // key is reviewer id
|
||||
originalReviewersMap := make(map[int64][]*Review) // key is original author id
|
||||
reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
|
||||
countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
|
||||
for _, review := range reviews {
|
||||
if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
|
||||
if review.OriginalAuthorID != 0 {
|
||||
originalReviewersMap[review.OriginalAuthorID] = append(originalReviewersMap[review.OriginalAuthorID], review)
|
||||
} else {
|
||||
reviewersMap[review.ReviewerID] = append(reviewersMap[review.ReviewerID], review)
|
||||
}
|
||||
} else if review.ReviewerTeamID != 0 && review.OriginalAuthorID == 0 {
|
||||
reviewTeamsMap[review.ReviewerTeamID] = append(reviewTeamsMap[review.ReviewerTeamID], review)
|
||||
}
|
||||
}
|
||||
|
||||
individualReviews := make([]*Review, 0, 10)
|
||||
for _, reviews := range reviewersMap {
|
||||
individualReviews = append(individualReviews, reviews[len(reviews)-1])
|
||||
}
|
||||
sort.Slice(individualReviews, func(i, j int) bool {
|
||||
return individualReviews[i].UpdatedUnix < individualReviews[j].UpdatedUnix
|
||||
})
|
||||
|
||||
originalReviews := make([]*Review, 0, 10)
|
||||
for _, reviews := range originalReviewersMap {
|
||||
originalReviews = append(originalReviews, reviews[len(reviews)-1])
|
||||
}
|
||||
sort.Slice(originalReviews, func(i, j int) bool {
|
||||
return originalReviews[i].UpdatedUnix < originalReviews[j].UpdatedUnix
|
||||
})
|
||||
|
||||
teamReviewRequests := make([]*Review, 0, 5)
|
||||
if err := sess.SQL("SELECT * FROM review WHERE id IN (SELECT max(id) as id FROM review WHERE issue_id = ? AND reviewer_team_id <> 0 AND original_author_id = 0 GROUP BY issue_id, reviewer_team_id) ORDER BY review.updated_unix ASC",
|
||||
issueID).
|
||||
Find(&teamReviewRequests); err != nil {
|
||||
return nil, err
|
||||
for _, reviews := range reviewTeamsMap {
|
||||
teamReviewRequests = append(teamReviewRequests, reviews[len(reviews)-1])
|
||||
}
|
||||
sort.Slice(teamReviewRequests, func(i, j int) bool {
|
||||
return teamReviewRequests[i].UpdatedUnix < teamReviewRequests[j].UpdatedUnix
|
||||
})
|
||||
|
||||
if len(teamReviewRequests) > 0 {
|
||||
reviews = append(reviews, teamReviewRequests...)
|
||||
}
|
||||
|
||||
return reviews, nil
|
||||
return append(individualReviews, teamReviewRequests...), originalReviews, nil
|
||||
}
|
||||
@@ -162,8 +162,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
},
|
||||
)
|
||||
|
||||
allReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
|
||||
allReviews, migratedReviews, err := issues_model.GetReviewsByIssueID(db.DefaultContext, issue.ID)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, migratedReviews)
|
||||
for _, review := range allReviews {
|
||||
assert.NoError(t, review.LoadReviewer(db.DefaultContext))
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"code.gitea.io/gitea/models/migrations/v1_21"
|
||||
"code.gitea.io/gitea/models/migrations/v1_22"
|
||||
"code.gitea.io/gitea/models/migrations/v1_23"
|
||||
"code.gitea.io/gitea/models/migrations/v1_24"
|
||||
"code.gitea.io/gitea/models/migrations/v1_6"
|
||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
||||
@@ -369,6 +370,9 @@ func prepareMigrationTasks() []*migration {
|
||||
newMigration(309, "Improve Notification table indices", v1_23.ImproveNotificationTableIndices),
|
||||
newMigration(310, "Add Priority to ProtectedBranch", v1_23.AddPriorityToProtectedBranch),
|
||||
newMigration(311, "Add TimeEstimate to Issue table", v1_23.AddTimeEstimateColumnToIssueTable),
|
||||
|
||||
// Gitea 1.23.0-rc0 ends at migration ID number 311 (database version 312)
|
||||
newMigration(312, "Add DeleteBranchAfterMerge to AutoMerge", v1_24.AddDeleteBranchAfterMergeForAutoMerge),
|
||||
}
|
||||
return preparedMigrations
|
||||
}
|
||||
|
||||
@@ -172,7 +172,7 @@ func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
|
||||
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
|
||||
}
|
||||
|
||||
u, err := giturl.Parse(remoteURL)
|
||||
u, err := giturl.ParseGitURL(remoteURL)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_24 //nolint
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
type pullAutoMerge struct {
|
||||
DeleteBranchAfterMerge bool
|
||||
}
|
||||
|
||||
// TableName return database table name for xorm
|
||||
func (pullAutoMerge) TableName() string {
|
||||
return "pull_auto_merge"
|
||||
}
|
||||
|
||||
func AddDeleteBranchAfterMergeForAutoMerge(x *xorm.Engine) error {
|
||||
return x.Sync(new(pullAutoMerge))
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUser_IsOwnedBy(t *testing.T) {
|
||||
@@ -180,9 +181,8 @@ func TestRestrictedUserOrgMembers(t *testing.T) {
|
||||
ID: 29,
|
||||
IsRestricted: true,
|
||||
})
|
||||
if !assert.True(t, restrictedUser.IsRestricted) {
|
||||
return // ensure fixtures return restricted user
|
||||
}
|
||||
// ensure fixtures return restricted user
|
||||
require.True(t, restrictedUser.IsRestricted)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
|
||||
+14
-12
@@ -15,13 +15,14 @@ import (
|
||||
|
||||
// AutoMerge represents a pull request scheduled for merging when checks succeed
|
||||
type AutoMerge struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
PullID int64 `xorm:"UNIQUE"`
|
||||
DoerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Doer *user_model.User `xorm:"-"`
|
||||
MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
|
||||
Message string `xorm:"LONGTEXT"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
PullID int64 `xorm:"UNIQUE"`
|
||||
DoerID int64 `xorm:"INDEX NOT NULL"`
|
||||
Doer *user_model.User `xorm:"-"`
|
||||
MergeStyle repo_model.MergeStyle `xorm:"varchar(30)"`
|
||||
Message string `xorm:"LONGTEXT"`
|
||||
DeleteBranchAfterMerge bool
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"created"`
|
||||
}
|
||||
|
||||
// TableName return database table name for xorm
|
||||
@@ -49,7 +50,7 @@ func IsErrAlreadyScheduledToAutoMerge(err error) bool {
|
||||
}
|
||||
|
||||
// ScheduleAutoMerge schedules a pull request to be merged when all checks succeed
|
||||
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string) error {
|
||||
func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64, style repo_model.MergeStyle, message string, deleteBranchAfterMerge bool) error {
|
||||
// Check if we already have a merge scheduled for that pull request
|
||||
if exists, _, err := GetScheduledMergeByPullID(ctx, pullID); err != nil {
|
||||
return err
|
||||
@@ -58,10 +59,11 @@ func ScheduleAutoMerge(ctx context.Context, doer *user_model.User, pullID int64,
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).Insert(&AutoMerge{
|
||||
DoerID: doer.ID,
|
||||
PullID: pullID,
|
||||
MergeStyle: style,
|
||||
Message: message,
|
||||
DoerID: doer.ID,
|
||||
PullID: pullID,
|
||||
MergeStyle: style,
|
||||
Message: message,
|
||||
DeleteBranchAfterMerge: deleteBranchAfterMerge,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
+41
-49
@@ -20,6 +20,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
@@ -279,6 +280,8 @@ func (repo *Repository) IsBroken() bool {
|
||||
}
|
||||
|
||||
// MarkAsBrokenEmpty marks the repo as broken and empty
|
||||
// FIXME: the status "broken" and "is_empty" were abused,
|
||||
// The code always set them together, no way to distinguish whether a repo is really "empty" or "broken"
|
||||
func (repo *Repository) MarkAsBrokenEmpty() {
|
||||
repo.Status = RepositoryBroken
|
||||
repo.IsEmpty = true
|
||||
@@ -635,14 +638,26 @@ type CloneLink struct {
|
||||
}
|
||||
|
||||
// ComposeHTTPSCloneURL returns HTTPS clone URL based on given owner and repository name.
|
||||
func ComposeHTTPSCloneURL(owner, repo string) string {
|
||||
return fmt.Sprintf("%s%s/%s.git", setting.AppURL, url.PathEscape(owner), url.PathEscape(repo))
|
||||
func ComposeHTTPSCloneURL(ctx context.Context, owner, repo string) string {
|
||||
return fmt.Sprintf("%s%s/%s.git", httplib.GuessCurrentAppURL(ctx), url.PathEscape(owner), url.PathEscape(repo))
|
||||
}
|
||||
|
||||
func ComposeSSHCloneURL(ownerName, repoName string) string {
|
||||
func ComposeSSHCloneURL(doer *user_model.User, ownerName, repoName string) string {
|
||||
sshUser := setting.SSH.User
|
||||
sshDomain := setting.SSH.Domain
|
||||
|
||||
if sshUser == "(DOER_USERNAME)" {
|
||||
// Some users use SSH reverse-proxy and need to use the current signed-in username as the SSH user
|
||||
// to make the SSH reverse-proxy could prepare the user's public keys ahead.
|
||||
// For most cases we have the correct "doer", then use it as the SSH user.
|
||||
// If we can't get the doer, then use the built-in SSH user.
|
||||
if doer != nil {
|
||||
sshUser = doer.Name
|
||||
} else {
|
||||
sshUser = setting.SSH.BuiltinServerUser
|
||||
}
|
||||
}
|
||||
|
||||
// non-standard port, it must use full URI
|
||||
if setting.SSH.Port != 22 {
|
||||
sshHost := net.JoinHostPort(sshDomain, strconv.Itoa(setting.SSH.Port))
|
||||
@@ -660,21 +675,20 @@ func ComposeSSHCloneURL(ownerName, repoName string) string {
|
||||
return fmt.Sprintf("%s@%s:%s/%s.git", sshUser, sshHost, url.PathEscape(ownerName), url.PathEscape(repoName))
|
||||
}
|
||||
|
||||
func (repo *Repository) cloneLink(isWiki bool) *CloneLink {
|
||||
repoName := repo.Name
|
||||
if isWiki {
|
||||
repoName += ".wiki"
|
||||
}
|
||||
|
||||
func (repo *Repository) cloneLink(ctx context.Context, doer *user_model.User, repoPathName string) *CloneLink {
|
||||
cl := new(CloneLink)
|
||||
cl.SSH = ComposeSSHCloneURL(repo.OwnerName, repoName)
|
||||
cl.HTTPS = ComposeHTTPSCloneURL(repo.OwnerName, repoName)
|
||||
cl.SSH = ComposeSSHCloneURL(doer, repo.OwnerName, repoPathName)
|
||||
cl.HTTPS = ComposeHTTPSCloneURL(ctx, repo.OwnerName, repoPathName)
|
||||
return cl
|
||||
}
|
||||
|
||||
// CloneLink returns clone URLs of repository.
|
||||
func (repo *Repository) CloneLink() (cl *CloneLink) {
|
||||
return repo.cloneLink(false)
|
||||
func (repo *Repository) CloneLink(ctx context.Context, doer *user_model.User) (cl *CloneLink) {
|
||||
return repo.cloneLink(ctx, doer, repo.Name)
|
||||
}
|
||||
|
||||
func (repo *Repository) CloneLinkGeneral(ctx context.Context) (cl *CloneLink) {
|
||||
return repo.cloneLink(ctx, nil /* no doer, use a general git user */, repo.Name)
|
||||
}
|
||||
|
||||
// GetOriginalURLHostname returns the hostname of a URL or the URL
|
||||
@@ -770,47 +784,25 @@ func GetRepositoryByName(ctx context.Context, ownerID int64, name string) (*Repo
|
||||
return &repo, err
|
||||
}
|
||||
|
||||
// getRepositoryURLPathSegments returns segments (owner, reponame) extracted from a url
|
||||
func getRepositoryURLPathSegments(repoURL string) []string {
|
||||
if strings.HasPrefix(repoURL, setting.AppURL) {
|
||||
return strings.Split(strings.TrimPrefix(repoURL, setting.AppURL), "/")
|
||||
}
|
||||
|
||||
sshURLVariants := [4]string{
|
||||
setting.SSH.Domain + ":",
|
||||
setting.SSH.User + "@" + setting.SSH.Domain + ":",
|
||||
"git+ssh://" + setting.SSH.Domain + "/",
|
||||
"git+ssh://" + setting.SSH.User + "@" + setting.SSH.Domain + "/",
|
||||
}
|
||||
|
||||
for _, sshURL := range sshURLVariants {
|
||||
if strings.HasPrefix(repoURL, sshURL) {
|
||||
return strings.Split(strings.TrimPrefix(repoURL, sshURL), "/")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRepositoryByURL returns the repository by given url
|
||||
func GetRepositoryByURL(ctx context.Context, repoURL string) (*Repository, error) {
|
||||
// possible urls for git:
|
||||
// https://my.domain/sub-path/<owner>/<repo>.git
|
||||
// https://my.domain/sub-path/<owner>/<repo>
|
||||
// git+ssh://user@my.domain/<owner>/<repo>.git
|
||||
// git+ssh://user@my.domain/<owner>/<repo>
|
||||
// user@my.domain:<owner>/<repo>.git
|
||||
// user@my.domain:<owner>/<repo>
|
||||
|
||||
pathSegments := getRepositoryURLPathSegments(repoURL)
|
||||
|
||||
if len(pathSegments) != 2 {
|
||||
ret, err := giturl.ParseRepositoryURL(ctx, repoURL)
|
||||
if err != nil || ret.OwnerName == "" {
|
||||
return nil, fmt.Errorf("unknown or malformed repository URL")
|
||||
}
|
||||
return GetRepositoryByOwnerAndName(ctx, ret.OwnerName, ret.RepoName)
|
||||
}
|
||||
|
||||
ownerName := pathSegments[0]
|
||||
repoName := strings.TrimSuffix(pathSegments[1], ".git")
|
||||
return GetRepositoryByOwnerAndName(ctx, ownerName, repoName)
|
||||
// GetRepositoryByURLRelax also accepts an SSH clone URL without user part
|
||||
func GetRepositoryByURLRelax(ctx context.Context, repoURL string) (*Repository, error) {
|
||||
if !strings.Contains(repoURL, "://") && !strings.Contains(repoURL, "@") {
|
||||
// convert "example.com:owner/repo" to "@example.com:owner/repo"
|
||||
p1, p2, p3 := strings.Index(repoURL, "."), strings.Index(repoURL, ":"), strings.Index(repoURL, "/")
|
||||
if 0 < p1 && p1 < p2 && p2 < p3 {
|
||||
repoURL = "@" + repoURL
|
||||
}
|
||||
}
|
||||
return GetRepositoryByURL(ctx, repoURL)
|
||||
}
|
||||
|
||||
// GetRepositoryByID returns the repository by given id if exists.
|
||||
|
||||
+35
-44
@@ -16,6 +16,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -132,60 +133,43 @@ func TestGetRepositoryByURL(t *testing.T) {
|
||||
|
||||
t.Run("InvalidPath", func(t *testing.T) {
|
||||
repo, err := GetRepositoryByURL(db.DefaultContext, "something")
|
||||
|
||||
assert.Nil(t, repo)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
testRepo2 := func(t *testing.T, url string) {
|
||||
repo, err := GetRepositoryByURL(db.DefaultContext, url)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(t, 2, repo.ID)
|
||||
assert.EqualValues(t, 2, repo.OwnerID)
|
||||
}
|
||||
|
||||
t.Run("ValidHttpURL", func(t *testing.T) {
|
||||
test := func(t *testing.T, url string) {
|
||||
repo, err := GetRepositoryByURL(db.DefaultContext, url)
|
||||
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "https://try.gitea.io/user2/repo2")
|
||||
test(t, "https://try.gitea.io/user2/repo2.git")
|
||||
testRepo2(t, "https://try.gitea.io/user2/repo2")
|
||||
testRepo2(t, "https://try.gitea.io/user2/repo2.git")
|
||||
})
|
||||
|
||||
t.Run("ValidGitSshURL", func(t *testing.T) {
|
||||
test := func(t *testing.T, url string) {
|
||||
repo, err := GetRepositoryByURL(db.DefaultContext, url)
|
||||
testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
|
||||
testRepo2(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
|
||||
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2")
|
||||
test(t, "git+ssh://sshuser@try.gitea.io/user2/repo2.git")
|
||||
|
||||
test(t, "git+ssh://try.gitea.io/user2/repo2")
|
||||
test(t, "git+ssh://try.gitea.io/user2/repo2.git")
|
||||
testRepo2(t, "git+ssh://try.gitea.io/user2/repo2")
|
||||
testRepo2(t, "git+ssh://try.gitea.io/user2/repo2.git")
|
||||
})
|
||||
|
||||
t.Run("ValidImplicitSshURL", func(t *testing.T) {
|
||||
test := func(t *testing.T, url string) {
|
||||
repo, err := GetRepositoryByURL(db.DefaultContext, url)
|
||||
|
||||
assert.NotNil(t, repo)
|
||||
assert.NoError(t, err)
|
||||
testRepo2(t, "sshuser@try.gitea.io:user2/repo2")
|
||||
testRepo2(t, "sshuser@try.gitea.io:user2/repo2.git")
|
||||
|
||||
testRelax := func(t *testing.T, url string) {
|
||||
repo, err := GetRepositoryByURLRelax(db.DefaultContext, url)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), repo.ID)
|
||||
assert.Equal(t, int64(2), repo.OwnerID)
|
||||
}
|
||||
|
||||
test(t, "sshuser@try.gitea.io:user2/repo2")
|
||||
test(t, "sshuser@try.gitea.io:user2/repo2.git")
|
||||
|
||||
test(t, "try.gitea.io:user2/repo2")
|
||||
test(t, "try.gitea.io:user2/repo2.git")
|
||||
// TODO: it doesn't seem to be common git ssh URL, should we really support this?
|
||||
testRelax(t, "try.gitea.io:user2/repo2")
|
||||
testRelax(t, "try.gitea.io:user2/repo2.git")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -199,23 +183,30 @@ func TestComposeSSHCloneURL(t *testing.T) {
|
||||
setting.SSH.Domain = "domain"
|
||||
setting.SSH.Port = 22
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "git@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
|
||||
setting.Repository.UseCompatSSHURI = true
|
||||
assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "ssh://git@domain/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
|
||||
// test SSH_DOMAIN while use non-standard SSH port
|
||||
setting.SSH.Port = 123
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
|
||||
setting.Repository.UseCompatSSHURI = true
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "ssh://git@domain:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
|
||||
|
||||
// test IPv6 SSH_DOMAIN
|
||||
setting.Repository.UseCompatSSHURI = false
|
||||
setting.SSH.Domain = "::1"
|
||||
setting.SSH.Port = 22
|
||||
assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "git@[::1]:user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
|
||||
setting.SSH.Port = 123
|
||||
assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL("user", "repo"))
|
||||
assert.Equal(t, "ssh://git@[::1]:123/user/repo.git", ComposeSSHCloneURL(nil, "user", "repo"))
|
||||
|
||||
setting.SSH.User = "(DOER_USERNAME)"
|
||||
setting.SSH.Domain = "domain"
|
||||
setting.SSH.Port = 22
|
||||
assert.Equal(t, "doer@domain:user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
|
||||
setting.SSH.Port = 123
|
||||
assert.Equal(t, "ssh://doer@domain:123/user/repo.git", ComposeSSHCloneURL(&user_model.User{Name: "doer"}, "user", "repo"))
|
||||
}
|
||||
|
||||
func TestIsUsableRepoName(t *testing.T) {
|
||||
|
||||
@@ -46,6 +46,12 @@ func UpdateRepositoryCols(ctx context.Context, repo *Repository, cols ...string)
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateRepositoryColsNoAutoTime updates repository's columns and but applies time change automatically
|
||||
func UpdateRepositoryColsNoAutoTime(ctx context.Context, repo *Repository, cols ...string) error {
|
||||
_, err := db.GetEngine(ctx).ID(repo.ID).Cols(cols...).NoAutoTime().Update(repo)
|
||||
return err
|
||||
}
|
||||
|
||||
// ErrReachLimitOfRepo represents a "ReachLimitOfRepo" kind of error.
|
||||
type ErrReachLimitOfRepo struct {
|
||||
Limit int
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -72,8 +73,8 @@ func (err ErrWikiInvalidFileName) Unwrap() error {
|
||||
}
|
||||
|
||||
// WikiCloneLink returns clone URLs of repository wiki.
|
||||
func (repo *Repository) WikiCloneLink() *CloneLink {
|
||||
return repo.cloneLink(true)
|
||||
func (repo *Repository) WikiCloneLink(ctx context.Context, doer *user_model.User) *CloneLink {
|
||||
return repo.cloneLink(ctx, doer, repo.Name+".wiki")
|
||||
}
|
||||
|
||||
// WikiPath returns wiki data path by given user and repository name.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package repo_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@@ -18,7 +19,7 @@ func TestRepository_WikiCloneLink(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
cloneLink := repo.WikiCloneLink()
|
||||
cloneLink := repo.WikiCloneLink(context.Background(), nil)
|
||||
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
|
||||
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
|
||||
|
||||
setting.IsInTesting = true
|
||||
setting.AppURL = "https://try.gitea.io/"
|
||||
setting.Domain = "try.gitea.io"
|
||||
setting.RunUser = "runuser"
|
||||
setting.SSH.User = "sshuser"
|
||||
setting.SSH.BuiltinServerUser = "builtinuser"
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetUserOpenIDs(t *testing.T) {
|
||||
@@ -34,30 +35,23 @@ func TestGetUserOpenIDs(t *testing.T) {
|
||||
func TestToggleUserOpenIDVisibility(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
oids, err := user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
|
||||
if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Len(t, oids, 1)
|
||||
assert.True(t, oids[0].Show)
|
||||
|
||||
err = user_model.ToggleUserOpenIDVisibility(db.DefaultContext, oids[0].ID)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
oids, err = user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
|
||||
if !assert.NoError(t, err) || !assert.Len(t, oids, 1) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Len(t, oids, 1)
|
||||
|
||||
assert.False(t, oids[0].Show)
|
||||
err = user_model.ToggleUserOpenIDVisibility(db.DefaultContext, oids[0].ID)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
oids, err = user_model.GetUserOpenIDs(db.DefaultContext, int64(2))
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
if assert.Len(t, oids, 1) {
|
||||
assert.True(t, oids[0].Show)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
package analyze
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsVendor(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -33,9 +37,8 @@ func TestIsVendor(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.path, func(t *testing.T) {
|
||||
if got := IsVendor(tt.path); got != tt.want {
|
||||
t.Errorf("IsVendor() = %v, want %v", got, tt.want)
|
||||
}
|
||||
got := IsVendor(tt.path)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,9 @@ package openid
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type testDiscoveredInfo struct{}
|
||||
@@ -29,21 +32,17 @@ func TestTimedDiscoveryCache(t *testing.T) {
|
||||
dc.Put("foo", &testDiscoveredInfo{}) // openid.opEndpoint: "a", openid.opLocalID: "b", openid.claimedID: "c"})
|
||||
|
||||
// Make sure we can retrieve them
|
||||
if di := dc.Get("foo"); di == nil {
|
||||
t.Errorf("Expected a result, got nil")
|
||||
} else if di.OpEndpoint() != "opEndpoint" || di.OpLocalID() != "opLocalID" || di.ClaimedID() != "claimedID" {
|
||||
t.Errorf("Expected opEndpoint opLocalID claimedID, got %v %v %v", di.OpEndpoint(), di.OpLocalID(), di.ClaimedID())
|
||||
}
|
||||
di := dc.Get("foo")
|
||||
require.NotNil(t, di)
|
||||
assert.Equal(t, "opEndpoint", di.OpEndpoint())
|
||||
assert.Equal(t, "opLocalID", di.OpLocalID())
|
||||
assert.Equal(t, "claimedID", di.ClaimedID())
|
||||
|
||||
// Attempt to get a non-existent value
|
||||
if di := dc.Get("bar"); di != nil {
|
||||
t.Errorf("Expected nil, got %v", di)
|
||||
}
|
||||
assert.Nil(t, dc.Get("bar"))
|
||||
|
||||
// Sleep one second and try retrieve again
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
if di := dc.Get("foo"); di != nil {
|
||||
t.Errorf("Expected a nil, got a result")
|
||||
}
|
||||
assert.Nil(t, dc.Get("foo"))
|
||||
}
|
||||
@@ -15,5 +15,5 @@ func TestPamAuth(t *testing.T) {
|
||||
result, err := Auth("gitea", "user1", "false-pwd")
|
||||
assert.Error(t, err)
|
||||
assert.EqualError(t, err, "Authentication failure")
|
||||
assert.Len(t, result)
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
@@ -4,46 +4,57 @@
|
||||
package pwn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/h2non/gock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var client = New(WithHTTP(&http.Client{
|
||||
Timeout: time.Second * 2,
|
||||
}))
|
||||
type mockTransport struct{}
|
||||
|
||||
func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.Host != "api.pwnedpasswords.com" {
|
||||
return nil, errors.New("unsupported host")
|
||||
}
|
||||
respMap := map[string]string{
|
||||
"/range/5c1d8": "EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2",
|
||||
"/range/ba189": "FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4",
|
||||
"/range/a1733": "C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0",
|
||||
"/range/5617b": "FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0",
|
||||
"/range/79082": "FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0",
|
||||
}
|
||||
if resp, ok := respMap[req.URL.Path]; ok {
|
||||
return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(resp))}, nil
|
||||
}
|
||||
return nil, errors.New("unsupported path")
|
||||
}
|
||||
|
||||
func TestPassword(t *testing.T) {
|
||||
defer gock.Off()
|
||||
client := New(WithHTTP(&http.Client{Transport: mockTransport{}}))
|
||||
|
||||
count, err := client.CheckPassword("", false)
|
||||
assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword")
|
||||
assert.Equal(t, -1, count)
|
||||
|
||||
gock.New("https://api.pwnedpasswords.com").Get("/range/5c1d8").Times(1).Reply(200).BodyString("EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2")
|
||||
count, err = client.CheckPassword("pwned", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
gock.New("https://api.pwnedpasswords.com").Get("/range/ba189").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4")
|
||||
count, err = client.CheckPassword("notpwned", false)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
gock.New("https://api.pwnedpasswords.com").Get("/range/a1733").Times(1).Reply(200).BodyString("C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0")
|
||||
count, err = client.CheckPassword("paddedpwned", true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 1, count)
|
||||
|
||||
gock.New("https://api.pwnedpasswords.com").Get("/range/5617b").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0")
|
||||
count, err = client.CheckPassword("paddednotpwned", true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
gock.New("https://api.pwnedpasswords.com").Get("/range/79082").Times(1).Reply(200).BodyString("FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0")
|
||||
count, err = client.CheckPassword("paddednotpwnedzero", true)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 0, count)
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
package emoji
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -22,32 +21,18 @@ func TestLookup(t *testing.T) {
|
||||
c := FromAlias(":beer:")
|
||||
d := FromAlias("beer")
|
||||
|
||||
if !reflect.DeepEqual(a, b) {
|
||||
t.Errorf("a and b should equal")
|
||||
}
|
||||
if !reflect.DeepEqual(b, c) {
|
||||
t.Errorf("b and c should equal")
|
||||
}
|
||||
if !reflect.DeepEqual(c, d) {
|
||||
t.Errorf("c and d should equal")
|
||||
}
|
||||
if !reflect.DeepEqual(a, d) {
|
||||
t.Errorf("a and d should equal")
|
||||
}
|
||||
assert.Equal(t, a, b)
|
||||
assert.Equal(t, b, c)
|
||||
assert.Equal(t, c, d)
|
||||
assert.Equal(t, a, d)
|
||||
|
||||
m := FromCode("\U0001f44d")
|
||||
n := FromAlias(":thumbsup:")
|
||||
o := FromAlias("+1")
|
||||
|
||||
if !reflect.DeepEqual(m, n) {
|
||||
t.Errorf("m and n should equal")
|
||||
}
|
||||
if !reflect.DeepEqual(n, o) {
|
||||
t.Errorf("n and o should equal")
|
||||
}
|
||||
if !reflect.DeepEqual(m, o) {
|
||||
t.Errorf("m and o should equal")
|
||||
}
|
||||
assert.Equal(t, m, n)
|
||||
assert.Equal(t, m, o)
|
||||
assert.Equal(t, n, o)
|
||||
}
|
||||
|
||||
func TestReplacers(t *testing.T) {
|
||||
@@ -61,9 +46,7 @@ func TestReplacers(t *testing.T) {
|
||||
|
||||
for i, x := range tests {
|
||||
s := x.f(x.v)
|
||||
if s != x.exp {
|
||||
t.Errorf("test %d `%s` expected `%s`, got: `%s`", i, x.v, x.exp, s)
|
||||
}
|
||||
assert.Equalf(t, x.exp, s, "test %d `%s` expected `%s`, got: `%s`", i, x.v, x.exp, s)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ package eventsource
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_wrapNewlines(t *testing.T) {
|
||||
@@ -38,16 +41,10 @@ func Test_wrapNewlines(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
w := &bytes.Buffer{}
|
||||
gotSum, err := wrapNewlines(w, []byte(tt.prefix), []byte(tt.value))
|
||||
if err != nil {
|
||||
t.Errorf("wrapNewlines() error = %v", err)
|
||||
return
|
||||
}
|
||||
if gotSum != int64(len(tt.output)) {
|
||||
t.Errorf("wrapNewlines() = %v, want %v", gotSum, int64(len(tt.output)))
|
||||
}
|
||||
if gotW := w.String(); gotW != tt.output {
|
||||
t.Errorf("wrapNewlines() = %v, want %v", gotW, tt.output)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, len(tt.output), gotSum)
|
||||
assert.Equal(t, tt.output, w.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -17,9 +17,7 @@ func TestBlob_Data(t *testing.T) {
|
||||
output := "file2\n"
|
||||
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
|
||||
repo, err := openRepositoryWithDefaultContext(bareRepo1Path)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
require.NoError(t, err)
|
||||
defer repo.Close()
|
||||
|
||||
testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375")
|
||||
|
||||
@@ -7,5 +7,5 @@ package git
|
||||
type CommitInfo struct {
|
||||
Entry *TreeEntry
|
||||
Commit *Commit
|
||||
SubModuleFile *CommitSubModuleFile
|
||||
SubmoduleFile *CommitSubmoduleFile
|
||||
}
|
||||
@@ -85,8 +85,8 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
|
||||
} else if subModule != nil {
|
||||
subModuleURL = subModule.URL
|
||||
}
|
||||
subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String())
|
||||
commitsInfo[i].SubModuleFile = subModuleFile
|
||||
subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
|
||||
commitsInfo[i].SubmoduleFile = subModuleFile
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,8 +79,8 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
|
||||
} else if subModule != nil {
|
||||
subModuleURL = subModule.URL
|
||||
}
|
||||
subModuleFile := NewCommitSubModuleFile(subModuleURL, entry.ID.String())
|
||||
commitsInfo[i].SubModuleFile = subModuleFile
|
||||
subModuleFile := NewCommitSubmoduleFile(subModuleURL, entry.ID.String())
|
||||
commitsInfo[i].SubmoduleFile = subModuleFile
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommitsCountSha256(t *testing.T) {
|
||||
@@ -94,9 +95,7 @@ signed commit`
|
||||
|
||||
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotNil(t, commitFromReader) {
|
||||
return
|
||||
}
|
||||
require.NotNil(t, commitFromReader)
|
||||
assert.EqualValues(t, sha, commitFromReader.ID)
|
||||
assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
|
||||
package git
|
||||
|
||||
type SubmoduleWebLink struct {
|
||||
RepoWebLink, CommitWebLink string
|
||||
}
|
||||
|
||||
// GetSubModules get all the submodules of current revision git tree
|
||||
func (c *Commit) GetSubModules() (*ObjectCache[*SubModule], error) {
|
||||
if c.submoduleCache != nil {
|
||||
|
||||
@@ -5,107 +5,50 @@
|
||||
package git
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"path"
|
||||
"regexp"
|
||||
"strings"
|
||||
"context"
|
||||
|
||||
giturl "code.gitea.io/gitea/modules/git/url"
|
||||
)
|
||||
|
||||
var scpSyntax = regexp.MustCompile(`^([a-zA-Z0-9_]+@)?([a-zA-Z0-9._-]+):(.*)$`)
|
||||
|
||||
// CommitSubModuleFile represents a file with submodule type.
|
||||
type CommitSubModuleFile struct {
|
||||
refURL string
|
||||
refID string
|
||||
// CommitSubmoduleFile represents a file with submodule type.
|
||||
type CommitSubmoduleFile struct {
|
||||
refURL string
|
||||
parsedURL *giturl.RepositoryURL
|
||||
parsed bool
|
||||
refID string
|
||||
repoLink string
|
||||
}
|
||||
|
||||
// NewCommitSubModuleFile create a new submodule file
|
||||
func NewCommitSubModuleFile(refURL, refID string) *CommitSubModuleFile {
|
||||
return &CommitSubModuleFile{
|
||||
refURL: refURL,
|
||||
refID: refID,
|
||||
}
|
||||
// NewCommitSubmoduleFile create a new submodule file
|
||||
func NewCommitSubmoduleFile(refURL, refID string) *CommitSubmoduleFile {
|
||||
return &CommitSubmoduleFile{refURL: refURL, refID: refID}
|
||||
}
|
||||
|
||||
func getRefURL(refURL, urlPrefix, repoFullName, sshDomain string) string {
|
||||
if refURL == "" {
|
||||
return ""
|
||||
func (sf *CommitSubmoduleFile) RefID() string {
|
||||
return sf.refID // this function is only used in templates
|
||||
}
|
||||
|
||||
// SubmoduleWebLink tries to make some web links for a submodule, it also works on "nil" receiver
|
||||
func (sf *CommitSubmoduleFile) SubmoduleWebLink(ctx context.Context, optCommitID ...string) *SubmoduleWebLink {
|
||||
if sf == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
refURI := strings.TrimSuffix(refURL, ".git")
|
||||
|
||||
prefixURL, _ := url.Parse(urlPrefix)
|
||||
urlPrefixHostname, _, err := net.SplitHostPort(prefixURL.Host)
|
||||
if err != nil {
|
||||
urlPrefixHostname = prefixURL.Host
|
||||
}
|
||||
|
||||
urlPrefix = strings.TrimSuffix(urlPrefix, "/")
|
||||
|
||||
// FIXME: Need to consider branch - which will require changes in modules/git/commit.go:GetSubModules
|
||||
// Relative url prefix check (according to git submodule documentation)
|
||||
if strings.HasPrefix(refURI, "./") || strings.HasPrefix(refURI, "../") {
|
||||
return urlPrefix + path.Clean(path.Join("/", repoFullName, refURI))
|
||||
}
|
||||
|
||||
if !strings.Contains(refURI, "://") {
|
||||
// scp style syntax which contains *no* port number after the : (and is not parsed by net/url)
|
||||
// ex: git@try.gitea.io:go-gitea/gitea
|
||||
match := scpSyntax.FindAllStringSubmatch(refURI, -1)
|
||||
if len(match) > 0 {
|
||||
m := match[0]
|
||||
refHostname := m[2]
|
||||
pth := m[3]
|
||||
|
||||
if !strings.HasPrefix(pth, "/") {
|
||||
pth = "/" + pth
|
||||
}
|
||||
|
||||
if urlPrefixHostname == refHostname || refHostname == sshDomain {
|
||||
return urlPrefix + path.Clean(path.Join("/", pth))
|
||||
}
|
||||
return "http://" + refHostname + pth
|
||||
if !sf.parsed {
|
||||
sf.parsed = true
|
||||
parsedURL, err := giturl.ParseRepositoryURL(ctx, sf.refURL)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
sf.parsedURL = parsedURL
|
||||
sf.repoLink = giturl.MakeRepositoryWebLink(sf.parsedURL)
|
||||
}
|
||||
|
||||
ref, err := url.Parse(refURI)
|
||||
if err != nil {
|
||||
return ""
|
||||
var commitLink string
|
||||
if len(optCommitID) == 2 {
|
||||
commitLink = sf.repoLink + "/compare/" + optCommitID[0] + "..." + optCommitID[1]
|
||||
} else if len(optCommitID) == 1 {
|
||||
commitLink = sf.repoLink + "/commit/" + optCommitID[0]
|
||||
} else {
|
||||
commitLink = sf.repoLink + "/commit/" + sf.refID
|
||||
}
|
||||
|
||||
refHostname, _, err := net.SplitHostPort(ref.Host)
|
||||
if err != nil {
|
||||
refHostname = ref.Host
|
||||
}
|
||||
|
||||
supportedSchemes := []string{"http", "https", "git", "ssh", "git+ssh"}
|
||||
|
||||
for _, scheme := range supportedSchemes {
|
||||
if ref.Scheme == scheme {
|
||||
if ref.Scheme == "http" || ref.Scheme == "https" {
|
||||
if len(ref.User.Username()) > 0 {
|
||||
return ref.Scheme + "://" + fmt.Sprintf("%v", ref.User) + "@" + ref.Host + ref.Path
|
||||
}
|
||||
return ref.Scheme + "://" + ref.Host + ref.Path
|
||||
} else if urlPrefixHostname == refHostname || refHostname == sshDomain {
|
||||
return urlPrefix + path.Clean(path.Join("/", ref.Path))
|
||||
}
|
||||
return "http://" + refHostname + ref.Path
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// RefURL guesses and returns reference URL.
|
||||
// FIXME: template passes AppURL as urlPrefix, it needs to figure out the correct approach (no hard-coded AppURL anymore)
|
||||
func (sf *CommitSubModuleFile) RefURL(urlPrefix, repoFullName, sshDomain string) string {
|
||||
return getRefURL(sf.refURL, urlPrefix, repoFullName, sshDomain)
|
||||
}
|
||||
|
||||
// RefID returns reference ID.
|
||||
func (sf *CommitSubModuleFile) RefID() string {
|
||||
return sf.refID
|
||||
return &SubmoduleWebLink{RepoWebLink: sf.repoLink, CommitWebLink: commitLink}
|
||||
}
|
||||
@@ -1,42 +1,30 @@
|
||||
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCommitSubModuleFileGetRefURL(t *testing.T) {
|
||||
kases := []struct {
|
||||
refURL string
|
||||
prefixURL string
|
||||
parentPath string
|
||||
SSHDomain string
|
||||
expect string
|
||||
}{
|
||||
{"git://github.com/user1/repo1", "/", "user1/repo2", "", "http://github.com/user1/repo1"},
|
||||
{"https://localhost/user1/repo1.git", "/", "user1/repo2", "", "https://localhost/user1/repo1"},
|
||||
{"http://localhost/user1/repo1.git", "/", "owner/reponame", "", "http://localhost/user1/repo1"},
|
||||
{"git@github.com:user1/repo1.git", "/", "owner/reponame", "", "http://github.com/user1/repo1"},
|
||||
{"ssh://git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/zefie/lge_g6_kernel_scripts"},
|
||||
{"git@git.zefie.net:2222/zefie/lge_g6_kernel_scripts.git", "/", "zefie/lge_g6_kernel", "", "http://git.zefie.net/2222/zefie/lge_g6_kernel_scripts"},
|
||||
{"git@try.gitea.io:go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
|
||||
{"ssh://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
|
||||
{"git://git@try.gitea.io:9999/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "", "https://try.gitea.io/go-gitea/gitea"},
|
||||
{"ssh://git@127.0.0.1:9999/go-gitea/gitea", "https://127.0.0.1:3000/", "go-gitea/sdk", "", "https://127.0.0.1:3000/go-gitea/gitea"},
|
||||
{"https://gitea.com:3000/user1/repo1.git", "https://127.0.0.1:3000/", "user/repo2", "", "https://gitea.com:3000/user1/repo1"},
|
||||
{"https://example.gitea.com/gitea/user1/repo1.git", "https://example.gitea.com/gitea/", "", "user/repo2", "https://example.gitea.com/gitea/user1/repo1"},
|
||||
{"https://username:password@github.com/username/repository.git", "/", "username/repository2", "", "https://username:password@github.com/username/repository"},
|
||||
{"somethingbad", "https://127.0.0.1:3000/go-gitea/gitea", "/", "", ""},
|
||||
{"git@localhost:user/repo", "https://localhost/", "user2/repo1", "", "https://localhost/user/repo"},
|
||||
{"../path/to/repo.git/", "https://localhost/", "user/repo2", "", "https://localhost/user/path/to/repo.git"},
|
||||
{"ssh://git@ssh.gitea.io:2222/go-gitea/gitea", "https://try.gitea.io/", "go-gitea/sdk", "ssh.gitea.io", "https://try.gitea.io/go-gitea/gitea"},
|
||||
}
|
||||
func TestCommitSubmoduleLink(t *testing.T) {
|
||||
sf := NewCommitSubmoduleFile("git@github.com:user/repo.git", "aaaa")
|
||||
|
||||
for _, kase := range kases {
|
||||
assert.EqualValues(t, kase.expect, getRefURL(kase.refURL, kase.prefixURL, kase.parentPath, kase.SSHDomain))
|
||||
}
|
||||
wl := sf.SubmoduleWebLink(context.Background())
|
||||
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
|
||||
assert.Equal(t, "https://github.com/user/repo/commit/aaaa", wl.CommitWebLink)
|
||||
|
||||
wl = sf.SubmoduleWebLink(context.Background(), "1111")
|
||||
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
|
||||
assert.Equal(t, "https://github.com/user/repo/commit/1111", wl.CommitWebLink)
|
||||
|
||||
wl = sf.SubmoduleWebLink(context.Background(), "1111", "2222")
|
||||
assert.Equal(t, "https://github.com/user/repo", wl.RepoWebLink)
|
||||
assert.Equal(t, "https://github.com/user/repo/compare/1111...2222", wl.CommitWebLink)
|
||||
|
||||
wl = (*CommitSubmoduleFile)(nil).SubmoduleWebLink(context.Background())
|
||||
assert.Nil(t, wl)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCommitsCount(t *testing.T) {
|
||||
@@ -91,9 +92,7 @@ empty commit`
|
||||
|
||||
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotNil(t, commitFromReader) {
|
||||
return
|
||||
}
|
||||
require.NotNil(t, commitFromReader)
|
||||
assert.EqualValues(t, sha, commitFromReader.ID)
|
||||
assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
@@ -159,9 +158,7 @@ ISO-8859-1`
|
||||
|
||||
commitFromReader, err := CommitFromReader(gitRepo, sha, strings.NewReader(commitString))
|
||||
assert.NoError(t, err)
|
||||
if !assert.NotNil(t, commitFromReader) {
|
||||
return
|
||||
}
|
||||
require.NotNil(t, commitFromReader)
|
||||
assert.EqualValues(t, sha, commitFromReader.ID)
|
||||
assert.EqualValues(t, `-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
|
||||
+10
-1
@@ -39,7 +39,7 @@ func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.Git
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return giturl.Parse(addr)
|
||||
return giturl.ParseGitURL(addr)
|
||||
}
|
||||
|
||||
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
|
||||
@@ -79,6 +79,15 @@ func (err *ErrInvalidCloneAddr) Unwrap() error {
|
||||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// IsRemoteNotExistError checks the prefix of the error message to see whether a remote does not exist.
|
||||
func IsRemoteNotExistError(err error) bool {
|
||||
// see: https://github.com/go-gitea/gitea/issues/32889#issuecomment-2571848216
|
||||
// Should not add space in the end, sometimes git will add a `:`
|
||||
prefix1 := "exit status 128 - fatal: No such remote" // git < 2.30
|
||||
prefix2 := "exit status 2 - error: No such remote" // git >= 2.30
|
||||
return strings.HasPrefix(err.Error(), prefix1) || strings.HasPrefix(err.Error(), prefix2)
|
||||
}
|
||||
|
||||
// ParseRemoteAddr checks if given remote address is valid,
|
||||
// and returns composed URL with needed username and password.
|
||||
func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, error) {
|
||||
|
||||
@@ -10,20 +10,18 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRepository_GetLanguageStats(t *testing.T) {
|
||||
repoPath := filepath.Join(testReposDir, "language_stats_repo")
|
||||
gitRepo, err := openRepositoryWithDefaultContext(repoPath)
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
defer gitRepo.Close()
|
||||
|
||||
stats, err := gitRepo.GetLanguageStats("8fee858da5796dfb37704761701bb8e800ad9ef3")
|
||||
if !assert.NoError(t, err) {
|
||||
t.Fatal()
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.EqualValues(t, map[string]int64{
|
||||
"Python": 134,
|
||||
|
||||
@@ -182,7 +182,6 @@ func TestRepository_GetAnnotatedTag(t *testing.T) {
|
||||
|
||||
// Annotated tag's name should fail
|
||||
tag3, err := bareRepo1.GetAnnotatedTag(aTagName)
|
||||
assert.Error(t, err)
|
||||
assert.Errorf(t, err, "Length must be 40: %d", len(aTagName))
|
||||
assert.Nil(t, tag3)
|
||||
|
||||
|
||||
+95
-3
@@ -4,9 +4,15 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
stdurl "net/url"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrWrongURLFormat represents an error with wrong url format
|
||||
@@ -21,7 +27,7 @@ func (err ErrWrongURLFormat) Error() string {
|
||||
// GitURL represents a git URL
|
||||
type GitURL struct {
|
||||
*stdurl.URL
|
||||
extraMark int // 0 no extra 1 scp 2 file path with no prefix
|
||||
extraMark int // 0: standard URL with scheme, 1: scp short syntax (no scheme), 2: file path with no prefix
|
||||
}
|
||||
|
||||
// String returns the URL's string
|
||||
@@ -38,8 +44,11 @@ func (u *GitURL) String() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse parse all kinds of git URL
|
||||
func Parse(remote string) (*GitURL, error) {
|
||||
// ParseGitURL parse all kinds of git URL:
|
||||
// * Full URL: http://git@host/path, http://git@host:port/path
|
||||
// * SCP short syntax: git@host:/path
|
||||
// * File path: /dir/repo/path
|
||||
func ParseGitURL(remote string) (*GitURL, error) {
|
||||
if strings.Contains(remote, "://") {
|
||||
u, err := stdurl.Parse(remote)
|
||||
if err != nil {
|
||||
@@ -87,3 +96,86 @@ func Parse(remote string) (*GitURL, error) {
|
||||
extraMark: 2,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type RepositoryURL struct {
|
||||
GitURL *GitURL
|
||||
|
||||
// if the URL belongs to current Gitea instance, then the below fields have values
|
||||
OwnerName string
|
||||
RepoName string
|
||||
RemainingPath string
|
||||
}
|
||||
|
||||
// ParseRepositoryURL tries to parse a Git URL and extract the owner/repository name if it belongs to current Gitea instance.
|
||||
func ParseRepositoryURL(ctx context.Context, repoURL string) (*RepositoryURL, error) {
|
||||
// possible urls for git:
|
||||
// https://my.domain/sub-path/<owner>/<repo>[.git]
|
||||
// git+ssh://user@my.domain/<owner>/<repo>[.git]
|
||||
// ssh://user@my.domain/<owner>/<repo>[.git]
|
||||
// user@my.domain:<owner>/<repo>[.git]
|
||||
parsed, err := ParseGitURL(repoURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := &RepositoryURL{}
|
||||
ret.GitURL = parsed
|
||||
|
||||
fillPathParts := func(s string) {
|
||||
s = strings.TrimPrefix(s, "/")
|
||||
fields := strings.SplitN(s, "/", 3)
|
||||
if len(fields) >= 2 {
|
||||
ret.OwnerName = fields[0]
|
||||
ret.RepoName = strings.TrimSuffix(fields[1], ".git")
|
||||
if len(fields) == 3 {
|
||||
ret.RemainingPath = "/" + fields[2]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if parsed.URL.Scheme == "http" || parsed.URL.Scheme == "https" {
|
||||
if !httplib.IsCurrentGiteaSiteURL(ctx, repoURL) {
|
||||
return ret, nil
|
||||
}
|
||||
fillPathParts(strings.TrimPrefix(parsed.URL.Path, setting.AppSubURL))
|
||||
} else if parsed.URL.Scheme == "ssh" || parsed.URL.Scheme == "git+ssh" {
|
||||
domainSSH := setting.SSH.Domain
|
||||
domainCur := httplib.GuessCurrentHostDomain(ctx)
|
||||
urlDomain, _, _ := net.SplitHostPort(parsed.URL.Host)
|
||||
urlDomain = util.IfZero(urlDomain, parsed.URL.Host)
|
||||
if urlDomain == "" {
|
||||
return ret, nil
|
||||
}
|
||||
// check whether URL domain is the App domain
|
||||
domainMatches := domainSSH == urlDomain
|
||||
// check whether URL domain is current domain from context
|
||||
domainMatches = domainMatches || (domainCur != "" && domainCur == urlDomain)
|
||||
if domainMatches {
|
||||
fillPathParts(parsed.URL.Path)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MakeRepositoryWebLink generates a web link (http/https) for a git repository (by guessing sometimes)
|
||||
func MakeRepositoryWebLink(repoURL *RepositoryURL) string {
|
||||
if repoURL.OwnerName != "" {
|
||||
return setting.AppSubURL + "/" + repoURL.OwnerName + "/" + repoURL.RepoName
|
||||
}
|
||||
|
||||
// now, let's guess, for example:
|
||||
// * git@github.com:owner/submodule.git
|
||||
// * https://github.com/example/submodule1.git
|
||||
if repoURL.GitURL.Scheme == "http" || repoURL.GitURL.Scheme == "https" {
|
||||
return strings.TrimSuffix(repoURL.GitURL.String(), ".git")
|
||||
} else if repoURL.GitURL.Scheme == "ssh" || repoURL.GitURL.Scheme == "git+ssh" {
|
||||
hostname, _, _ := net.SplitHostPort(repoURL.GitURL.Host)
|
||||
hostname = util.IfZero(hostname, repoURL.GitURL.Host)
|
||||
urlPath := strings.TrimSuffix(repoURL.GitURL.Path, ".git")
|
||||
urlPath = strings.TrimPrefix(urlPath, "/")
|
||||
urlFull := fmt.Sprintf("https://%s/%s", hostname, urlPath)
|
||||
urlFull = strings.TrimSuffix(urlFull, "/")
|
||||
return urlFull
|
||||
}
|
||||
return ""
|
||||
}
|
||||
+102
-1
@@ -4,9 +4,15 @@
|
||||
package url
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/httplib"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/test"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -157,10 +163,105 @@ func TestParseGitURLs(t *testing.T) {
|
||||
|
||||
for _, kase := range kases {
|
||||
t.Run(kase.kase, func(t *testing.T) {
|
||||
u, err := Parse(kase.kase)
|
||||
u, err := ParseGitURL(kase.kase)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, kase.expected.extraMark, u.extraMark)
|
||||
assert.EqualValues(t, *kase.expected, *u)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRepositoryURL(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000")()
|
||||
defer test.MockVariableValue(&setting.SSH.Domain, "try.gitea.io")()
|
||||
|
||||
ctxURL, _ := url.Parse("https://gitea")
|
||||
ctxReq := &http.Request{URL: ctxURL, Header: http.Header{}}
|
||||
ctxReq.Host = ctxURL.Host
|
||||
ctxReq.Header.Add("X-Forwarded-Proto", ctxURL.Scheme)
|
||||
ctx := context.WithValue(context.Background(), httplib.RequestContextKey, ctxReq)
|
||||
cases := []struct {
|
||||
input string
|
||||
ownerName, repoName, remaining string
|
||||
}{
|
||||
{input: "/user/repo"},
|
||||
|
||||
{input: "https://localhost:3000/user/repo", ownerName: "user", repoName: "repo"},
|
||||
{input: "https://external:3000/user/repo"},
|
||||
|
||||
{input: "https://localhost:3000/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
|
||||
|
||||
{input: "https://gitea/user/repo", ownerName: "user", repoName: "repo"},
|
||||
{input: "https://gitea:3333/user/repo"},
|
||||
|
||||
{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
|
||||
{input: "ssh://external:2222/user/repo"},
|
||||
|
||||
{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
|
||||
{input: "git+ssh://user@external/user/repo.git"},
|
||||
|
||||
{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
|
||||
{input: "root@gitea:user/repo.git", ownerName: "user", repoName: "repo"},
|
||||
{input: "root@external:user/repo.git"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.input, func(t *testing.T) {
|
||||
ret, _ := ParseRepositoryURL(ctx, c.input)
|
||||
assert.Equal(t, c.ownerName, ret.OwnerName)
|
||||
assert.Equal(t, c.repoName, ret.RepoName)
|
||||
assert.Equal(t, c.remaining, ret.RemainingPath)
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("WithSubpath", func(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/subpath")()
|
||||
cases = []struct {
|
||||
input string
|
||||
ownerName, repoName, remaining string
|
||||
}{
|
||||
{input: "https://localhost:3000/user/repo"},
|
||||
{input: "https://localhost:3000/subpath/user/repo.git/other", ownerName: "user", repoName: "repo", remaining: "/other"},
|
||||
|
||||
{input: "ssh://try.gitea.io:2222/user/repo", ownerName: "user", repoName: "repo"},
|
||||
{input: "ssh://external:2222/user/repo"},
|
||||
|
||||
{input: "git+ssh://user@try.gitea.io/user/repo.git", ownerName: "user", repoName: "repo"},
|
||||
{input: "git+ssh://user@external/user/repo.git"},
|
||||
|
||||
{input: "root@try.gitea.io:user/repo.git", ownerName: "user", repoName: "repo"},
|
||||
{input: "root@external:user/repo.git"},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
t.Run(c.input, func(t *testing.T) {
|
||||
ret, _ := ParseRepositoryURL(ctx, c.input)
|
||||
assert.Equal(t, c.ownerName, ret.OwnerName)
|
||||
assert.Equal(t, c.repoName, ret.RepoName)
|
||||
assert.Equal(t, c.remaining, ret.RemainingPath)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestMakeRepositoryBaseLink(t *testing.T) {
|
||||
defer test.MockVariableValue(&setting.AppURL, "https://localhost:3000/subpath")()
|
||||
defer test.MockVariableValue(&setting.AppSubURL, "/subpath")()
|
||||
|
||||
u, err := ParseRepositoryURL(context.Background(), "https://localhost:3000/subpath/user/repo.git")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "/subpath/user/repo", MakeRepositoryWebLink(u))
|
||||
|
||||
u, err = ParseRepositoryURL(context.Background(), "https://github.com/owner/repo.git")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u))
|
||||
|
||||
u, err = ParseRepositoryURL(context.Background(), "git@github.com:owner/repo.git")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://github.com/owner/repo", MakeRepositoryWebLink(u))
|
||||
|
||||
u, err = ParseRepositoryURL(context.Background(), "git+ssh://other:123/owner/repo.git")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "https://other/owner/repo", MakeRepositoryWebLink(u))
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func BenchmarkGetCommitGraph(b *testing.B) {
|
||||
@@ -235,9 +237,7 @@ func TestParseGlyphs(t *testing.T) {
|
||||
}
|
||||
row++
|
||||
}
|
||||
if len(parser.availableColors) != 9 {
|
||||
t.Errorf("Expected 9 colors but have %d", len(parser.availableColors))
|
||||
}
|
||||
assert.Len(t, parser.availableColors, 9)
|
||||
}
|
||||
|
||||
func TestCommitStringParsing(t *testing.T) {
|
||||
@@ -262,9 +262,7 @@ func TestCommitStringParsing(t *testing.T) {
|
||||
return
|
||||
}
|
||||
|
||||
if test.commitMessage != commit.Subject {
|
||||
t.Errorf("%s does not match %s", test.commitMessage, commit.Subject)
|
||||
}
|
||||
assert.Equal(t, test.commitMessage, commit.Subject)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func ParseSizeAndClass(defaultSize int, defaultClass string, others ...any) (int
|
||||
return size, class
|
||||
}
|
||||
|
||||
func HTMLFormat(s string, rawArgs ...any) template.HTML {
|
||||
func HTMLFormat(s template.HTML, rawArgs ...any) template.HTML {
|
||||
args := slices.Clone(rawArgs)
|
||||
for i, v := range args {
|
||||
switch v := v.(type) {
|
||||
@@ -44,5 +44,5 @@ func HTMLFormat(s string, rawArgs ...any) template.HTML {
|
||||
args[i] = template.HTMLEscapeString(fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
return template.HTML(fmt.Sprintf(s, args...))
|
||||
return template.HTML(fmt.Sprintf(string(s), args...))
|
||||
}
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestServeContentByReader(t *testing.T) {
|
||||
@@ -71,9 +72,7 @@ func TestServeContentByReadSeeker(t *testing.T) {
|
||||
}
|
||||
|
||||
seekReader, err := os.OpenFile(tmpFile, os.O_RDONLY, 0o644)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
defer seekReader.Close()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
|
||||
@@ -5,6 +5,7 @@ package httplib
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -81,6 +82,12 @@ func GuessCurrentHostURL(ctx context.Context) string {
|
||||
return reqScheme + "://" + req.Host
|
||||
}
|
||||
|
||||
func GuessCurrentHostDomain(ctx context.Context) string {
|
||||
_, host, _ := strings.Cut(GuessCurrentHostURL(ctx), "://")
|
||||
domain, _, _ := net.SplitHostPort(host)
|
||||
return util.IfZero(domain, host)
|
||||
}
|
||||
|
||||
// MakeAbsoluteURL tries to make a link to an absolute URL:
|
||||
// * If link is empty, it returns the current app URL.
|
||||
// * If link is absolute, it returns the link.
|
||||
@@ -105,7 +112,7 @@ func IsCurrentGiteaSiteURL(ctx context.Context, s string) bool {
|
||||
if cleanedPath == "" || cleanedPath == "." {
|
||||
u.Path = "/"
|
||||
} else {
|
||||
u.Path += "/" + cleanedPath + "/"
|
||||
u.Path = "/" + cleanedPath + "/"
|
||||
}
|
||||
}
|
||||
if urlIsRelative(s, u) {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/indexer/issues/internal/tests"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestElasticsearchIndexer(t *testing.T) {
|
||||
@@ -26,20 +28,10 @@ func TestElasticsearchIndexer(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
ok := false
|
||||
for i := 0; i < 60; i++ {
|
||||
require.Eventually(t, func() bool {
|
||||
resp, err := http.Get(url)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
t.Logf("Waiting for elasticsearch to be up: %v", err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Failed to wait for elasticsearch to be up")
|
||||
return
|
||||
}
|
||||
return err == nil && resp.StatusCode == http.StatusOK
|
||||
}, time.Minute, time.Second, "Expected elasticsearch to be up")
|
||||
|
||||
indexer := NewIndexer(url, fmt.Sprintf("test_elasticsearch_indexer_%d", time.Now().Unix()))
|
||||
defer indexer.Close()
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
_ "code.gitea.io/gitea/models/activities"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
@@ -26,7 +27,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestDBSearchIssues(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
setting.Indexer.IssueType = "db"
|
||||
InitIssueIndexer(true)
|
||||
@@ -83,9 +84,7 @@ func searchIssueWithKeyword(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -120,9 +119,7 @@ func searchIssueByIndex(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -166,9 +163,7 @@ func searchIssueInRepo(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -238,9 +233,7 @@ func searchIssueByID(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -265,9 +258,7 @@ func searchIssueIsPull(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -292,9 +283,7 @@ func searchIssueIsClosed(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -319,9 +308,7 @@ func searchIssueIsArchived(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -346,9 +333,7 @@ func searchIssueByMilestoneID(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -379,9 +364,7 @@ func searchIssueByLabelID(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -400,9 +383,7 @@ func searchIssueByTime(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -421,9 +402,7 @@ func searchIssueWithOrder(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -454,9 +433,7 @@ func searchIssueInProject(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, _, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
}
|
||||
}
|
||||
@@ -479,9 +456,7 @@ func searchIssueWithPaginator(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
issueIDs, total, err := SearchIssues(context.TODO(), &test.opts)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.expectedIDs, issueIDs)
|
||||
assert.Equal(t, test.expectedTotal, total)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/meilisearch/meilisearch-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMeilisearchIndexer(t *testing.T) {
|
||||
@@ -32,20 +33,10 @@ func TestMeilisearchIndexer(t *testing.T) {
|
||||
key = os.Getenv("TEST_MEILISEARCH_KEY")
|
||||
}
|
||||
|
||||
ok := false
|
||||
for i := 0; i < 60; i++ {
|
||||
require.Eventually(t, func() bool {
|
||||
resp, err := http.Get(url)
|
||||
if err == nil && resp.StatusCode == http.StatusOK {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
t.Logf("Waiting for meilisearch to be up: %v", err)
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("Failed to wait for meilisearch to be up")
|
||||
return
|
||||
}
|
||||
return err == nil && resp.StatusCode == http.StatusOK
|
||||
}, time.Minute, time.Second, "Expected meilisearch to be up")
|
||||
|
||||
indexer := NewIndexer(url, key, fmt.Sprintf("test_meilisearch_indexer_%d", time.Now().Unix()))
|
||||
defer indexer.Close()
|
||||
|
||||
@@ -957,9 +957,8 @@ func Test_minQuotes(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := minQuotes(tt.args.value); got != tt.want {
|
||||
t.Errorf("minQuotes() = %v, want %v", got, tt.want)
|
||||
}
|
||||
got := minQuotes(tt.args.value)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@ func (r *RenderInternal) ProtectSafeAttrs(content template.HTML) template.HTML {
|
||||
return template.HTML(reAttrClass().ReplaceAllString(string(content), `$1 data-attr-class="`+r.secureIDPrefix+`$2"$3`))
|
||||
}
|
||||
|
||||
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt string, a ...any) error {
|
||||
func (r *RenderInternal) FormatWithSafeAttrs(w io.Writer, fmt template.HTML, a ...any) error {
|
||||
_, err := w.Write([]byte(r.ProtectSafeAttrs(htmlutil.HTMLFormat(fmt, a...))))
|
||||
return err
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
package math
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
|
||||
"code.gitea.io/gitea/modules/markup/internal"
|
||||
giteaUtil "code.gitea.io/gitea/modules/util"
|
||||
|
||||
@@ -50,7 +52,7 @@ func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.N
|
||||
n := node.(*Block)
|
||||
if entering {
|
||||
code := giteaUtil.Iif(n.Inline, "", `<pre class="code-block is-loading">`) + `<code class="language-math display">`
|
||||
_ = r.renderInternal.FormatWithSafeAttrs(w, code)
|
||||
_ = r.renderInternal.FormatWithSafeAttrs(w, template.HTML(code))
|
||||
r.writeLines(w, source, n)
|
||||
} else {
|
||||
_, _ = w.WriteString(`</code>` + giteaUtil.Iif(n.Inline, "", `</pre>`) + "\n")
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -140,23 +142,13 @@ func TestRenderConfig_UnmarshalYAML(t *testing.T) {
|
||||
Icon: "table",
|
||||
Lang: "",
|
||||
}
|
||||
if err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got); err != nil {
|
||||
t.Errorf("RenderConfig.UnmarshalYAML() error = %v\n%q", err, tt.args)
|
||||
return
|
||||
}
|
||||
err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got)
|
||||
require.NoError(t, err)
|
||||
|
||||
if got.Meta != tt.expected.Meta {
|
||||
t.Errorf("Meta Expected %s Got %s", tt.expected.Meta, got.Meta)
|
||||
}
|
||||
if got.Icon != tt.expected.Icon {
|
||||
t.Errorf("Icon Expected %s Got %s", tt.expected.Icon, got.Icon)
|
||||
}
|
||||
if got.Lang != tt.expected.Lang {
|
||||
t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
|
||||
}
|
||||
if got.TOC != tt.expected.TOC {
|
||||
t.Errorf("TOC Expected %q Got %q", tt.expected.TOC, got.TOC)
|
||||
}
|
||||
assert.Equal(t, tt.expected.Meta, got.Meta)
|
||||
assert.Equal(t, tt.expected.Icon, got.Icon)
|
||||
assert.Equal(t, tt.expected.Lang, got.Lang)
|
||||
assert.Equal(t, tt.expected.TOC, got.TOC)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -147,7 +147,7 @@ func (r *orgWriter) resolveLink(kind, link string) string {
|
||||
func (r *orgWriter) WriteRegularLink(l org.RegularLink) {
|
||||
link := r.resolveLink(l.Kind(), l.URL)
|
||||
|
||||
printHTML := func(html string, a ...any) {
|
||||
printHTML := func(html template.HTML, a ...any) {
|
||||
_, _ = fmt.Fprint(r, htmlutil.HTMLFormat(html, a...))
|
||||
}
|
||||
// Inspired by https://github.com/niklasfasching/go-org/blob/6eb20dbda93cb88c3503f7508dc78cbbc639378f/org/html_writer.go#L406-L427
|
||||
|
||||
@@ -103,8 +103,8 @@ func HelloWorld() {
|
||||
}
|
||||
#+end_src
|
||||
`, `<div class="src src-go">
|
||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"
|
||||
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"</span>
|
||||
<span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||
<span class="p">}</span></code></pre>
|
||||
</div>`)
|
||||
|
||||
@@ -5,6 +5,9 @@ package nosql
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestToRedisURI(t *testing.T) {
|
||||
@@ -26,9 +29,9 @@ func TestToRedisURI(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ToRedisURI(tt.connection); got == nil || got.String() != tt.want {
|
||||
t.Errorf(`ToRedisURI(%q) = %s, want %s`, tt.connection, got.String(), tt.want)
|
||||
}
|
||||
got := ToRedisURI(tt.connection)
|
||||
require.NotNil(t, got)
|
||||
assert.Equal(t, tt.want, got.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -81,6 +81,7 @@ type PackageMetadataVersion struct {
|
||||
BundleDependencies []string `json:"bundleDependencies,omitempty"`
|
||||
DevDependencies map[string]string `json:"devDependencies,omitempty"`
|
||||
PeerDependencies map[string]string `json:"peerDependencies,omitempty"`
|
||||
PeerDependenciesMeta map[string]any `json:"peerDependenciesMeta,omitempty"`
|
||||
Bin map[string]string `json:"bin,omitempty"`
|
||||
OptionalDependencies map[string]string `json:"optionalDependencies,omitempty"`
|
||||
Readme string `json:"readme,omitempty"`
|
||||
@@ -222,6 +223,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
|
||||
BundleDependencies: meta.BundleDependencies,
|
||||
DevelopmentDependencies: meta.DevDependencies,
|
||||
PeerDependencies: meta.PeerDependencies,
|
||||
PeerDependenciesMeta: meta.PeerDependenciesMeta,
|
||||
OptionalDependencies: meta.OptionalDependencies,
|
||||
Bin: meta.Bin,
|
||||
Readme: meta.Readme,
|
||||
|
||||
@@ -19,6 +19,7 @@ type Metadata struct {
|
||||
BundleDependencies []string `json:"bundleDependencies,omitempty"`
|
||||
DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"`
|
||||
PeerDependencies map[string]string `json:"peer_dependencies,omitempty"`
|
||||
PeerDependenciesMeta map[string]any `json:"peer_dependencies_meta,omitempty"`
|
||||
OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"`
|
||||
Bin map[string]string `json:"bin,omitempty"`
|
||||
Readme string `json:"readme,omitempty"`
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
|
||||
"gitea.com/lunny/levelqueue"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
)
|
||||
|
||||
@@ -29,9 +30,7 @@ func TestCorruptedLevelQueue(t *testing.T) {
|
||||
// sometimes the levelqueue could be in a corrupted state, this test is to make sure it can recover from it
|
||||
dbDir := t.TempDir() + "/levelqueue-test"
|
||||
db, err := leveldb.OpenFile(dbDir, nil)
|
||||
if !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
defer db.Close()
|
||||
|
||||
assert.NoError(t, db.Put([]byte("other-key"), []byte("other-value"), nil))
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func waitRedisReady(conn string, dur time.Duration) (ready bool) {
|
||||
@@ -61,9 +62,7 @@ func TestBaseRedis(t *testing.T) {
|
||||
return
|
||||
}
|
||||
assert.NoError(t, redisServer.Start())
|
||||
if !assert.True(t, waitRedisReady("redis://127.0.0.1:6379/0", 5*time.Second), "start redis-server") {
|
||||
return
|
||||
}
|
||||
require.True(t, waitRedisReady("redis://127.0.0.1:6379/0", 5*time.Second), "start redis-server")
|
||||
}
|
||||
|
||||
testQueueBasic(t, newBaseRedisSimple, toBaseConfig("baseRedis", setting.QueueSettings{Length: 10}), false)
|
||||
|
||||
@@ -6,6 +6,7 @@ package repository
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
git_model "code.gitea.io/gitea/models/git"
|
||||
@@ -51,6 +52,9 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository,
|
||||
{
|
||||
branches, _, err := gitRepo.GetBranchNames(0, 0)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "ref file is empty") {
|
||||
return 0, nil
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
log.Trace("SyncRepoBranches[%s]: branches[%d]: %v", repo.FullName(), len(branches), branches)
|
||||
|
||||
@@ -5,6 +5,8 @@ package structs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNoBetterThan(t *testing.T) {
|
||||
@@ -166,9 +168,7 @@ func TestNoBetterThan(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.args.css.NoBetterThan(tt.args.css2)
|
||||
if result != tt.want {
|
||||
t.Errorf("NoBetterThan() = %v, want %v", result, tt.want)
|
||||
}
|
||||
assert.Equal(t, tt.want, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap {
|
||||
"Iif": iif,
|
||||
"Eval": evalTokens,
|
||||
"SafeHTML": safeHTML,
|
||||
"HTMLFormat": htmlutil.HTMLFormat,
|
||||
"HTMLFormat": htmlFormat,
|
||||
"HTMLEscape": htmlEscape,
|
||||
"QueryEscape": queryEscape,
|
||||
"QueryBuild": QueryBuild,
|
||||
@@ -207,6 +207,20 @@ func htmlEscape(s any) template.HTML {
|
||||
panic(fmt.Sprintf("unexpected type %T", s))
|
||||
}
|
||||
|
||||
func htmlFormat(s any, args ...any) template.HTML {
|
||||
if len(args) == 0 {
|
||||
// to prevent developers from calling "HTMLFormat $userInput" by mistake which will lead to XSS
|
||||
panic("missing arguments for HTMLFormat")
|
||||
}
|
||||
switch v := s.(type) {
|
||||
case string:
|
||||
return htmlutil.HTMLFormat(template.HTML(v), args...)
|
||||
case template.HTML:
|
||||
return htmlutil.HTMLFormat(v, args...)
|
||||
}
|
||||
panic(fmt.Sprintf("unexpected type %T", s))
|
||||
}
|
||||
|
||||
func jsEscapeSafe(s string) template.HTML {
|
||||
return template.HTML(template.JSEscapeString(s))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/htmlutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -88,7 +87,7 @@ func TestTemplateIif(t *testing.T) {
|
||||
func TestTemplateEscape(t *testing.T) {
|
||||
execTmpl := func(code string) string {
|
||||
tmpl := template.New("test")
|
||||
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlutil.HTMLFormat})
|
||||
tmpl.Funcs(template.FuncMap{"QueryBuild": QueryBuild, "HTMLFormat": htmlFormat})
|
||||
template.Must(tmpl.Parse(code))
|
||||
w := &strings.Builder{}
|
||||
assert.NoError(t, tmpl.Execute(w, nil))
|
||||
|
||||
@@ -150,7 +150,7 @@ func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteNa
|
||||
return ret
|
||||
}
|
||||
|
||||
u, err := giturl.Parse(remoteURL)
|
||||
u, err := giturl.ParseGitURL(remoteURL)
|
||||
if err != nil {
|
||||
log.Error("giturl.Parse %v", err)
|
||||
return ret
|
||||
|
||||
+10
-12
@@ -8,6 +8,9 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func getWhoamiOutput() (string, error) {
|
||||
@@ -20,24 +23,19 @@ func getWhoamiOutput() (string, error) {
|
||||
|
||||
func TestCurrentUsername(t *testing.T) {
|
||||
user := CurrentUsername()
|
||||
if len(user) == 0 {
|
||||
t.Errorf("expected non-empty user, got: %s", user)
|
||||
}
|
||||
require.NotEmpty(t, user)
|
||||
|
||||
// Windows whoami is weird, so just skip remaining tests
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("skipped test because of weird whoami on Windows")
|
||||
}
|
||||
whoami, err := getWhoamiOutput()
|
||||
if err != nil {
|
||||
t.Errorf("failed to run whoami to test current user: %f", err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
user = CurrentUsername()
|
||||
if user != whoami {
|
||||
t.Errorf("expected %s as user, got: %s", whoami, user)
|
||||
}
|
||||
assert.Equal(t, whoami, user)
|
||||
|
||||
t.Setenv("USER", "spoofed")
|
||||
user = CurrentUsername()
|
||||
if user != whoami {
|
||||
t.Errorf("expected %s as user, got: %s", whoami, user)
|
||||
}
|
||||
assert.Equal(t, whoami, user)
|
||||
}
|
||||
@@ -3,7 +3,11 @@
|
||||
|
||||
package util
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestShellEscape(t *testing.T) {
|
||||
tests := []struct {
|
||||
@@ -83,9 +87,7 @@ func TestShellEscape(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := ShellEscape(tt.toEscape); got != tt.want {
|
||||
t.Errorf("ShellEscape(%q):\nGot: %s\nWanted: %s", tt.toEscape, got, tt.want)
|
||||
}
|
||||
assert.Equal(t, tt.want, ShellEscape(tt.toEscape))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -89,11 +89,23 @@ func (p *routerPathMatcher) matchPath(chiCtx *chi.Context, path string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func isValidMethod(name string) bool {
|
||||
switch name {
|
||||
case http.MethodGet, http.MethodPost, http.MethodPut, http.MethodPatch, http.MethodDelete, http.MethodHead, http.MethodOptions, http.MethodConnect, http.MethodTrace:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func newRouterPathMatcher(methods, pattern string, h ...any) *routerPathMatcher {
|
||||
middlewares, handlerFunc := wrapMiddlewareAndHandler(nil, h)
|
||||
p := &routerPathMatcher{methods: make(container.Set[string]), middlewares: middlewares, handlerFunc: handlerFunc}
|
||||
for _, method := range strings.Split(methods, ",") {
|
||||
p.methods.Add(strings.TrimSpace(method))
|
||||
method = strings.TrimSpace(method)
|
||||
if !isValidMethod(method) {
|
||||
panic(fmt.Sprintf("invalid HTTP method: %s", method))
|
||||
}
|
||||
p.methods.Add(method)
|
||||
}
|
||||
re := []byte{'^'}
|
||||
lastEnd := 0
|
||||
|
||||
@@ -6,6 +6,8 @@ package routing
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_shortenFilename(t *testing.T) {
|
||||
@@ -37,9 +39,8 @@ func Test_shortenFilename(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(fmt.Sprintf("shortenFilename('%s')", tt.filename), func(t *testing.T) {
|
||||
if gotShort := shortenFilename(tt.filename, tt.fallback); gotShort != tt.expected {
|
||||
t.Errorf("shortenFilename('%s'), expect '%s', but get '%s'", tt.filename, tt.expected, gotShort)
|
||||
}
|
||||
gotShort := shortenFilename(tt.filename, tt.fallback)
|
||||
assert.Equal(t, tt.expected, gotShort)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -72,9 +73,8 @@ func Test_trimAnonymousFunctionSuffix(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := trimAnonymousFunctionSuffix(tt.name); got != tt.want {
|
||||
t.Errorf("trimAnonymousFunctionSuffix() = %v, want %v", got, tt.want)
|
||||
}
|
||||
got := trimAnonymousFunctionSuffix(tt.name)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -69,6 +69,10 @@ func (h HookEventType) Event() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h HookEventType) IsPullRequest() bool {
|
||||
return h.Event() == "pull_request"
|
||||
}
|
||||
|
||||
// HookType is the type of a webhook
|
||||
type HookType = string
|
||||
|
||||
|
||||
@@ -145,6 +145,7 @@ confirm_delete_selected=Potvrdit odstranění všech vybraných položek?
|
||||
|
||||
name=Název
|
||||
value=Hodnota
|
||||
readme=Readme
|
||||
|
||||
filter=Filtr
|
||||
filter.clear=Vymazat filtr
|
||||
@@ -243,6 +244,7 @@ license_desc=Vše je na <a target="_blank" rel="noopener noreferrer" href="%[1]s
|
||||
|
||||
[install]
|
||||
install=Instalace
|
||||
installing_desc=Probíhá instalace, čekejte prosím...
|
||||
title=Výchozí konfigurace
|
||||
docker_helper=Pokud spouštíte Gitea v Dockeru, přečtěte si <a target="_blank" rel="noopener noreferrer" href="%s">dokumentaci</a>, než budete měnit jakákoliv nastavení.
|
||||
require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
|
||||
@@ -459,6 +461,7 @@ authorize_application=Autorizovat aplikaci
|
||||
authorize_redirect_notice=Budete přesměrováni na %s, pokud autorizujete tuto aplikaci.
|
||||
authorize_application_created_by=Tuto aplikaci vytvořil %s.
|
||||
authorize_application_description=Pokud povolíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací.
|
||||
authorize_application_with_scopes=S rozsahy působnosti: %s
|
||||
authorize_title=Autorizovat „%s“ pro přístup k vašemu účtu?
|
||||
authorization_failed=Autorizace selhala
|
||||
authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný požadavek. Kontaktujte prosím správce aplikace, kterou jste se pokoušeli autorizovat.
|
||||
@@ -765,6 +768,7 @@ uploaded_avatar_not_a_image=Nahraný soubor není obrázek.
|
||||
uploaded_avatar_is_too_big=Nahraný soubor (%d KiB) přesahuje maximální velikost (%d KiB).
|
||||
update_avatar_success=Vaše avatar byl aktualizován.
|
||||
update_user_avatar_success=Uživatelův avatar byl aktualizován.
|
||||
cropper_prompt=Před uložením můžete obrázek upravit. Upravený obrázek bude uložen jako PNG.
|
||||
|
||||
change_password=Aktualizovat heslo
|
||||
old_password=Stávající heslo
|
||||
@@ -1012,6 +1016,9 @@ new_repo_helper=Repozitář obsahuje všechny projektové soubory, včetně hist
|
||||
owner=Vlastník
|
||||
owner_helper=Některé organizace se nemusejí v seznamu zobrazit kvůli maximálnímu dosaženému počtu repozitářů.
|
||||
repo_name=Název repozitáře
|
||||
repo_name_profile_public_hint=.profile je speciální repozitář, který můžete použít k přidání souboru README.md do svého veřejného profilu organizace, který je viditelný pro každého. Ujistěte se, že je veřejný, a pro začátek jej inicializujte pomocí README v adresáři profilu.
|
||||
repo_name_profile_private_hint=.profile-private je speciální repozitář, který můžete použít k přidání souboru README.md do profilu člena organizace, který je viditelný pouze pro členy organizace. Ujistěte se, že je soukromý, a pro začátek jej inicializujte pomocí README v adresáři profilu.
|
||||
repo_name_helper=Dobrá jména repozitářů používají krátká, zapamatovatelná a unikátní klíčová slova. Repozitář s názvem „.profile“ nebo „.profile-private“ lze použít k přidání README.md pro uživatelský/organizační profil.
|
||||
repo_size=Velikost repozitáře
|
||||
template=Šablona
|
||||
template_select=Vyberte šablonu.
|
||||
@@ -1030,6 +1037,8 @@ fork_to_different_account=Rozštěpit na jiný účet
|
||||
fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna.
|
||||
fork_branch=Větev, která má být klonována pro fork
|
||||
all_branches=Všechny větve
|
||||
view_all_branches=Zobrazit všechny větve
|
||||
view_all_tags=Zobrazit všechny značky
|
||||
fork_no_valid_owners=Tento repozitář nemůže být rozštěpen, protože neexistují žádní platní vlastníci.
|
||||
fork.blocked_user=Nelze rozštěpit repozitář, protože jste blokováni majitelem repozitáře.
|
||||
use_template=Použít tuto šablonu
|
||||
@@ -1041,6 +1050,8 @@ generate_repo=Generovat repozitář
|
||||
generate_from=Generovat z
|
||||
repo_desc=Popis
|
||||
repo_desc_helper=Zadejte krátký popis (volitelné)
|
||||
repo_no_desc=Nebyl uveden žádný popis
|
||||
repo_lang=Jazyky
|
||||
repo_gitignore_helper=Vyberte šablony .gitignore.
|
||||
repo_gitignore_helper_desc=Vyberte soubory, které nechcete sledovat ze seznamu šablon pro běžné jazyky. Typické artefakty generované nástroji pro sestavení každého jazyka jsou ve výchozím stavu součástí .gitignore.
|
||||
issue_labels=Štítky úkolů
|
||||
@@ -1102,6 +1113,7 @@ delete_preexisting_success=Smazány nepřijaté soubory v %s
|
||||
blame_prior=Zobrazit blame před touto změnou
|
||||
blame.ignore_revs=Ignorování revizí v <a href="%s">.git-blame-ignorerevs</a>. Klikněte zde <a href="%s">pro obejití</a> a zobrazení normálního pohledu blame.
|
||||
blame.ignore_revs.failed=Nepodařilo se ignorovat revize v <a href="%s">.git-blame-ignore-revs</a>.
|
||||
user_search_tooltip=Zobrazí maximálně 30 uživatelů
|
||||
|
||||
tree_path_not_found_commit=Cesta %[1]s v commitu %[2]s neexistuje
|
||||
tree_path_not_found_branch=Cesta %[1]s ve větvi %[2]s neexistuje
|
||||
@@ -1223,6 +1235,7 @@ create_new_repo_command=Vytvořit nový repozitář na příkazové řádce
|
||||
push_exist_repo=Nahrání existujícího repozitáře z příkazové řádky
|
||||
empty_message=Tento repozitář nemá žádný obsah.
|
||||
broken_message=Data gitu, která jsou základem tohoto repozitáře, nelze číst. Kontaktujte správce této instance nebo smažte tento repositář.
|
||||
no_branch=Tento repozitář nemá žádné větve.
|
||||
|
||||
code=Zdrojový kód
|
||||
code.desc=Přístup ke zdrojovým kódům, souborům, commitům a větvím.
|
||||
@@ -1520,6 +1533,8 @@ issues.filter_assignee=Zpracovatel
|
||||
issues.filter_assginee_no_select=Všichni zpracovatelé
|
||||
issues.filter_assginee_no_assignee=Bez zpracovatele
|
||||
issues.filter_poster=Autor
|
||||
issues.filter_user_placeholder=Hledat uživatele
|
||||
issues.filter_user_no_select=Všichni uživatelé
|
||||
issues.filter_type=Typ
|
||||
issues.filter_type.all_issues=Všechny úkoly
|
||||
issues.filter_type.assigned_to_you=Přiřazené vám
|
||||
@@ -1664,12 +1679,25 @@ issues.delete.title=Smazat tento úkol?
|
||||
issues.delete.text=Opravdu chcete tento úkol smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
|
||||
|
||||
issues.tracker=Sledování času
|
||||
issues.timetracker_timer_start=Spustit časovač
|
||||
issues.timetracker_timer_stop=Zastavit časovač
|
||||
issues.timetracker_timer_discard=Zahodit časovač
|
||||
issues.timetracker_timer_manually_add=Přidat čas
|
||||
|
||||
issues.time_estimate_set=Nastavit odhadovaný čas
|
||||
issues.time_estimate_display=Odhad: %s
|
||||
issues.change_time_estimate_at=změnil/a odhad času na <b>%s</b> %s
|
||||
issues.remove_time_estimate_at=odstranil/a odhad času %s
|
||||
issues.time_estimate_invalid=Formát odhadu času je neplatný
|
||||
issues.start_tracking_history=započal/a práci %s
|
||||
issues.tracker_auto_close=Časovač se automaticky zastaví po zavření tohoto úkolu
|
||||
issues.tracking_already_started=`Již jste spustili sledování času na <a href="%s">jiném úkolu</a>!`
|
||||
issues.stop_tracking_history=pracoval/a <b>%s</b> %s
|
||||
issues.cancel_tracking_history=`zrušil/a sledování času %s`
|
||||
issues.del_time=Odstranit tento časový záznam
|
||||
issues.add_time_history=přidal/a strávený čas <b>%s</b> %s
|
||||
issues.del_time_history=`odstranil/a strávený čas %s`
|
||||
issues.add_time_manually=Přidat čas ručně
|
||||
issues.add_time_hours=Hodiny
|
||||
issues.add_time_minutes=Minuty
|
||||
issues.add_time_sum_to_small=Čas nebyl zadán.
|
||||
@@ -1922,6 +1950,9 @@ pulls.delete.title=Odstranit tento pull request?
|
||||
pulls.delete.text=Opravdu chcete tento pull request smazat? (Tím se trvale odstraní veškerý obsah. Pokud jej hodláte archivovat, zvažte raději jeho uzavření.)
|
||||
|
||||
pulls.recently_pushed_new_branches=Nahráli jste větev <strong>%[1]s</strong> %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_1=Tato větev je %[1]d commit pozadu za %[2]s
|
||||
pulls.upstream_diverging_prompt_behind_n=Tato větev je %[1]d commitů pozadu za %[2]s
|
||||
pulls.upstream_diverging_prompt_base_newer=Hlavní větev %s má nové změny
|
||||
|
||||
pull.deleted_branch=(odstraněno):%s
|
||||
pull.agit_documentation=Prohlédněte si dokumentaci o AGit
|
||||
@@ -2595,6 +2626,9 @@ diff.image.overlay=Překrytí
|
||||
diff.has_escaped=Tento řádek má skryté znaky Unicode
|
||||
diff.show_file_tree=Zobrazit souborový strom
|
||||
diff.hide_file_tree=Skrýt souborový strom
|
||||
diff.submodule_added=Submodul %[1]s přidán v %[2]s
|
||||
diff.submodule_deleted=Submodul %[1]s odebrán z %[2]s
|
||||
diff.submodule_updated=Submodul %[1]s aktualizován: %[2]s
|
||||
|
||||
releases.desc=Sledování verzí projektu a souborů ke stažení.
|
||||
release.releases=Vydání
|
||||
@@ -2604,6 +2638,7 @@ release.new_release=Nové vydání
|
||||
release.draft=Koncept
|
||||
release.prerelease=Předběžná verze
|
||||
release.stable=Stabilní
|
||||
release.latest=Nejnovější
|
||||
release.compare=Porovnat
|
||||
release.edit=upravit
|
||||
release.ahead.commits=<strong>%d</strong> revizí
|
||||
@@ -2832,6 +2867,9 @@ teams.invite.title=Byli jste pozváni do týmu <strong>%s</strong> v organizaci
|
||||
teams.invite.by=Pozvání od %s
|
||||
teams.invite.description=Pro připojení k týmu klikněte na tlačítko níže.
|
||||
|
||||
view_as_role=Zobrazit jako: %s
|
||||
view_as_public_hint=Prohlížíte README jako veřejný uživatel.
|
||||
view_as_member_hint=Prohlížíte README jako člen této organizace.
|
||||
|
||||
[admin]
|
||||
maintenance=Údržba
|
||||
@@ -3502,6 +3540,7 @@ versions=Verze
|
||||
versions.view_all=Zobrazit všechny
|
||||
dependency.id=ID
|
||||
dependency.version=Verze
|
||||
search_in_external_registry=Hledat v %s
|
||||
alpine.registry=Nastavte tento registr přidáním URL do <code>/etc/apk/repositories</code>:
|
||||
alpine.registry.key=Stáhněte si veřejný RSA klíč registru do složky <code>/etc/apk/keys/</code> pro ověření podpisu indexu:
|
||||
alpine.registry.info=Vyberte $branch a $repository ze seznamu níže.
|
||||
@@ -3510,6 +3549,8 @@ alpine.repository=Informace o repozitáři
|
||||
alpine.repository.branches=Větve
|
||||
alpine.repository.repositories=Repozitáře
|
||||
alpine.repository.architectures=Architektury
|
||||
arch.registry=Přidejte server se souvisejícím repozitářem a architekturou do <code>/etc/pacman.conf</code>:
|
||||
arch.install=Synchronizovat balíček s pacman:
|
||||
arch.repository=Informace o repozitáři
|
||||
arch.repository.repositories=Repozitáře
|
||||
arch.repository.architectures=Architektury
|
||||
@@ -3692,6 +3733,7 @@ runners.status.active=Aktivní
|
||||
runners.status.offline=Offline
|
||||
runners.version=Verze
|
||||
runners.reset_registration_token=Resetovat registrační token
|
||||
runners.reset_registration_token_confirm=Chcete zneplatnit stávající token a vygenerovat nový?
|
||||
runners.reset_registration_token_success=Registrační token runneru byl úspěšně obnoven
|
||||
|
||||
runs.all_workflows=Všechny pracovní postupy
|
||||
@@ -3724,6 +3766,7 @@ workflow.not_found=Pracovní postup „%s“ nebyl nalezen.
|
||||
workflow.run_success=Pracovní postup „%s“ proběhl úspěšně.
|
||||
workflow.from_ref=Použít pracovní postup od
|
||||
workflow.has_workflow_dispatch=Tento pracovní postup má spouštěč události workflow_dispatch.
|
||||
workflow.has_no_workflow_dispatch=Pracovní postup „%s“ memá workflow_dispatch spouštěč.
|
||||
|
||||
need_approval_desc=Potřebujete schválení pro spuštění pracovních postupů pro rozštěpený pull request.
|
||||
|
||||
@@ -3743,6 +3786,8 @@ variables.creation.success=Proměnná „%s“ byla přidána.
|
||||
variables.update.failed=Úprava proměnné se nezdařila.
|
||||
variables.update.success=Proměnná byla upravena.
|
||||
|
||||
logs.always_auto_scroll=Vždy automaticky posouvat logy
|
||||
logs.always_expand_running=Vždy rozšířit běžící logy
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Odstraněný projekt
|
||||
|
||||
@@ -1235,6 +1235,7 @@ create_new_repo_command = Creating a new repository on the command line
|
||||
push_exist_repo = Pushing an existing repository from the command line
|
||||
empty_message = This repository does not contain any content.
|
||||
broken_message = The Git data underlying this repository cannot be read. Contact the administrator of this instance or delete this repository.
|
||||
no_branch = This repository doesn’t have any branches.
|
||||
|
||||
code = Code
|
||||
code.desc = Access source code, files, commits and branches.
|
||||
@@ -2627,6 +2628,9 @@ diff.image.overlay = Overlay
|
||||
diff.has_escaped = This line has hidden Unicode characters
|
||||
diff.show_file_tree = Show file tree
|
||||
diff.hide_file_tree = Hide file tree
|
||||
diff.submodule_added = Submodule %[1]s added at %[2]s
|
||||
diff.submodule_deleted = Submodule %[1]s deleted from %[2]s
|
||||
diff.submodule_updated = Submodule %[1]s updated: %[2]s
|
||||
|
||||
releases.desc = Track project versions and downloads.
|
||||
release.releases = Releases
|
||||
@@ -3764,6 +3768,7 @@ workflow.not_found = Workflow '%s' not found.
|
||||
workflow.run_success = Workflow '%s' run successfully.
|
||||
workflow.from_ref = Use workflow from
|
||||
workflow.has_workflow_dispatch = This workflow has a workflow_dispatch event trigger.
|
||||
workflow.has_no_workflow_dispatch = Workflow '%s' has no workflow_dispatch event trigger.
|
||||
|
||||
need_approval_desc = Need approval to run workflows for fork pull request.
|
||||
|
||||
|
||||
@@ -244,6 +244,7 @@ license_desc=Venez récupérer <a target="_blank" rel="noopener noreferrer" href
|
||||
|
||||
[install]
|
||||
install=Installation
|
||||
installing_desc=Installation en cours, veuillez patienter…
|
||||
title=Configuration initiale
|
||||
docker_helper=Si vous exécutez Gitea dans Docker, veuillez lire la <a target="_blank" rel="noopener noreferrer" href="%s">documentation</a> avant de modifier les paramètres.
|
||||
require_db_desc=Gitea nécessite MySQL, PostgreSQL, MSSQL, SQLite3 ou TiDB (avec le protocole MySQL).
|
||||
@@ -1015,6 +1016,9 @@ new_repo_helper=Un dépôt contient tous les fichiers d’un projet, ainsi que l
|
||||
owner=Propriétaire
|
||||
owner_helper=Certaines organisations peuvent ne pas apparaître dans la liste déroulante en raison d'une limite maximale du nombre de dépôts.
|
||||
repo_name=Nom du dépôt
|
||||
repo_name_profile_public_hint=.profile est un dépôt spécial que vous pouvez utiliser pour ajouter un README.md à votre profil public d’organisation, visible à tout le monde. Assurez-vous qu’il soit public et initialisez-le avec un README dans le répertoire de profil pour commencer.
|
||||
repo_name_profile_private_hint=.profile-private est un dépôt spécial que vous pouvez utiliser pour ajouter un README.md à votre profil d’organisation, visible uniquement aux membres de l’organisation. Assurez-vous qu’il soit privé et initialisez-le avec un README dans le répertoire de profil pour commencer.
|
||||
repo_name_helper=Idéalement, le nom d’un dépôt devrait être court, mémorisable et unique. Vous pouvez personnaliser votre profil ou celui de votre organisation en créant un dépôt nommé « .profile » ou « .profile-private » et contenant un README.md.
|
||||
repo_size=Taille du dépôt
|
||||
template=Modèle
|
||||
template_select=Répliquer un modèle
|
||||
@@ -1231,6 +1235,7 @@ create_new_repo_command=Création d'un nouveau dépôt en ligne de commande
|
||||
push_exist_repo=Soumission d'un dépôt existant par ligne de commande
|
||||
empty_message=Ce dépôt n'a pas de contenu.
|
||||
broken_message=Les données git de ce dépôt ne peuvent pas être lues. Contactez l'administrateur de cette instance ou supprimez ce dépôt.
|
||||
no_branch=Ce dépôt n’a aucune branche.
|
||||
|
||||
code=Code
|
||||
code.desc=Accéder au code source, fichiers, révisions et branches.
|
||||
@@ -2860,6 +2865,9 @@ teams.invite.title=Vous avez été invité à rejoindre l'équipe <strong>%s</st
|
||||
teams.invite.by=Invité par %s
|
||||
teams.invite.description=Veuillez cliquer sur le bouton ci-dessous pour rejoindre l’équipe.
|
||||
|
||||
view_as_role=Voir en tant que %s
|
||||
view_as_public_hint=Vous visualisez le README en tant qu’utilisateur public.
|
||||
view_as_member_hint=Vous visualisez le README en tant que membre de cette organisation.
|
||||
|
||||
[admin]
|
||||
maintenance=Maintenance
|
||||
@@ -3530,6 +3538,7 @@ versions=Versions
|
||||
versions.view_all=Voir tout
|
||||
dependency.id=ID
|
||||
dependency.version=Version
|
||||
search_in_external_registry=Rechercher dans %s
|
||||
alpine.registry=Configurez ce registre en ajoutant l’URL dans votre fichier <code>/etc/apk/repositories</code> :
|
||||
alpine.registry.key=Téléchargez la clé RSA publique du registre dans le dossier <code>/etc/apk/keys/</code> pour vérifier la signature de l'index :
|
||||
alpine.registry.info=Choisissez $branch et $repository dans la liste ci-dessous.
|
||||
@@ -3722,6 +3731,7 @@ runners.status.active=Actif
|
||||
runners.status.offline=Hors-ligne
|
||||
runners.version=Version
|
||||
runners.reset_registration_token=Réinitialiser le jeton d'enregistrement
|
||||
runners.reset_registration_token_confirm=Voulez-vous révoquer le jeton actuel et en générer un nouveau ?
|
||||
runners.reset_registration_token_success=Le jeton d’inscription de l’exécuteur a été réinitialisé avec succès
|
||||
|
||||
runs.all_workflows=Tous les flux de travail
|
||||
@@ -3754,6 +3764,7 @@ workflow.not_found=Flux de travail « %s » introuvable.
|
||||
workflow.run_success=Le flux de travail « %s » s’est correctement exécuté.
|
||||
workflow.from_ref=Utiliser le flux de travail depuis
|
||||
workflow.has_workflow_dispatch=Ce flux de travail a un déclencheur d’événement workflow_dispatch.
|
||||
workflow.has_no_workflow_dispatch=Le flux de travail %s n’a pas de déclencheur d’événement workflow_dispatch.
|
||||
|
||||
need_approval_desc=Besoin d’approbation pour exécuter des flux de travail pour une demande d’ajout de bifurcation.
|
||||
|
||||
@@ -3773,6 +3784,8 @@ variables.creation.success=La variable « %s » a été ajoutée.
|
||||
variables.update.failed=Impossible d’éditer la variable.
|
||||
variables.update.success=La variable a bien été modifiée.
|
||||
|
||||
logs.always_auto_scroll=Toujours faire défiler les journaux automatiquement
|
||||
logs.always_expand_running=Toujours développer les journaux en cours
|
||||
|
||||
[projects]
|
||||
deleted.display_name=Projet supprimé
|
||||
|
||||
@@ -3763,6 +3763,8 @@ variables.creation.success=変数 "%s" を追加しました。
|
||||
variables.update.failed=変数を更新できませんでした。
|
||||
variables.update.success=変数を更新しました。
|
||||
|
||||
logs.always_auto_scroll=常にログを自動スクロール
|
||||
logs.always_expand_running=常に実行中のログを展開
|
||||
|
||||
[projects]
|
||||
deleted.display_name=削除されたプロジェクト
|
||||
|
||||
@@ -111,7 +111,7 @@ license=오픈 소스
|
||||
[install]
|
||||
install=설치
|
||||
title=초기 설정
|
||||
docker_helper="Gitea를 Docker에서 실행하려면 설정 전에 이 <a target=\"_blank\" rel=\"noopener noreferrer\" href=\"%s\">문서</a>를 읽어보세요."
|
||||
docker_helper=Gitea를 Docker에서 실행하려면 설정 전에 이 <a target="_blank" rel="noopener noreferrer" href="%s">문서</a>를 읽어보세요.
|
||||
db_title=데이터베이스 설정
|
||||
db_type=데이터베이스 유형
|
||||
host=호스트
|
||||
@@ -436,8 +436,8 @@ manage_gpg_keys=GPG 키 관리
|
||||
add_key=키 추가
|
||||
ssh_desc=이러한 SSH 공용 키는 귀하의 계정과 연결되어 있습니다. 해당 개인 키는 당신의 저장소에 대한 전체 액세스를 가능하게 합니다.
|
||||
gpg_desc=이러한 GPG 공개키는 당신의 계정과 연결되어있습니다. 커밋이 검증될 수 있도록 당신의 개인 키를 안전하게 유지하십시오.
|
||||
ssh_helper="<strong>도움이 필요하세요?</strong> GitHub의 설명서를 참조하시기 바랍니다: <a href=\"%s\">SSH 키 생성하기</a> 또는 SSH를 사용할 때 <a href=\"%s\">일반적인 문제</a>"
|
||||
gpg_helper="<strong>도움이 필요하세요?</strong> GitHub의 설명서를 참조하시기 바랍니다: <a href=\"%s\">GPG키에 대하여</a>."
|
||||
ssh_helper=<strong>도움이 필요하세요?</strong> GitHub의 설명서를 참조하시기 바랍니다: <a href="%s">SSH 키 생성하기</a> 또는 SSH를 사용할 때 <a href="%s">일반적인 문제</a>
|
||||
gpg_helper=<strong>도움이 필요하세요?</strong> GitHub의 설명서를 참조하시기 바랍니다: <a href="%s">GPG키에 대하여</a>.
|
||||
add_new_key=SSH 키 추가
|
||||
add_new_gpg_key=GPG 키 추가
|
||||
gpg_key_id_used=같은 ID의 GPG 공개키가 이미 존재합니다.
|
||||
@@ -538,7 +538,7 @@ template_helper=템플릿으로 저장소 만들기
|
||||
visibility=가시성
|
||||
visibility_helper_forced=사이트 관리자가 새 레포지토리에 대해 비공개로만 생성되도록 하였습니다.
|
||||
visibility_fork_helper=(변경사항을 적용하는 경우 모든 포크가 영향을 받게 됩니다.)
|
||||
clone_helper="클론하는데에 도움이 필요하면 <a target=\"_blank\" href=\"%s\">Help</a>에 방문하세요."
|
||||
clone_helper=클론하는데에 도움이 필요하면 <a target="_blank" href="%s">Help</a>에 방문하세요.
|
||||
fork_repo=저장소 포크
|
||||
fork_from=원본 프로젝트 :
|
||||
fork_visibility_helper=포크된 저장소의 가시성은 변경하실 수 없습니다.
|
||||
@@ -640,7 +640,7 @@ editor.or=혹은
|
||||
editor.cancel_lower=취소
|
||||
editor.commit_changes=변경 내용을 커밋
|
||||
editor.commit_message_desc=선택적 확장 설명을 추가...
|
||||
editor.commit_directly_to_this_branch="<strong class=\"branch-name\">%s</strong> 브랜치에서 직접 커밋해주세요."
|
||||
editor.commit_directly_to_this_branch=<strong class="branch-name">%s</strong> 브랜치에서 직접 커밋해주세요.
|
||||
editor.create_new_branch=이 커밋에 대한 <strong>새로운 브랜치</strong>를 만들고 끌어오기 요청을 시작합니다.
|
||||
editor.new_branch_name_desc=새로운 브랜치 명...
|
||||
editor.cancel=취소
|
||||
@@ -667,7 +667,7 @@ ext_issues.desc=외부 이슈 트래커 연결.
|
||||
projects.description_placeholder=설명
|
||||
projects.title=제목
|
||||
projects.new=새 프로젝트
|
||||
projects.template.desc="템플릿"
|
||||
projects.template.desc=템플릿
|
||||
projects.column.edit_title=이름
|
||||
projects.column.new_title=이름
|
||||
|
||||
@@ -731,7 +731,7 @@ issues.action_milestone=마일스톤
|
||||
issues.action_milestone_no_select=마일스톤 없음
|
||||
issues.action_assignee=담당자
|
||||
issues.action_assignee_no_select=담당자 없음
|
||||
issues.opened_by="<a href=\"%[2]s\"> %[3]s</a>가 %[1]s을 오픈"
|
||||
issues.opened_by=<a href="%[2]s"> %[3]s</a>가 %[1]s을 오픈
|
||||
issues.previous=이전
|
||||
issues.next=다음
|
||||
issues.open_title=오픈
|
||||
@@ -747,7 +747,7 @@ issues.create_comment=코멘트
|
||||
issues.commit_ref_at=` 커밋 <a id="%[1]s" href="#%[1]s">%[2]s</a>에서 이 이슈 언급`
|
||||
issues.role.owner=소유자
|
||||
issues.role.member=멤버
|
||||
issues.sign_in_require_desc="<a href=\"%s\">로그인</a>하여 이 대화에 참여"
|
||||
issues.sign_in_require_desc=<a href="%s">로그인</a>하여 이 대화에 참여
|
||||
issues.edit=수정
|
||||
issues.cancel=취소
|
||||
issues.save=저장
|
||||
@@ -780,9 +780,9 @@ issues.time_spent_total=총 경과된 시간
|
||||
issues.time_spent_from_all_authors=`총 경과된 시간: %s`
|
||||
|
||||
issues.due_date=마감일
|
||||
issues.invalid_due_date_format="마감일은 반드시 'yyyy-mm-dd' 형식이어야 합니다."
|
||||
issues.error_modifying_due_date="마감일 수정을 실패하였습니다."
|
||||
issues.error_removing_due_date="마감일 삭제를 실패하였습니다."
|
||||
issues.invalid_due_date_format=마감일은 반드시 'yyyy-mm-dd' 형식이어야 합니다.
|
||||
issues.error_modifying_due_date=마감일 수정을 실패하였습니다.
|
||||
issues.error_removing_due_date=마감일 삭제를 실패하였습니다.
|
||||
issues.due_date_form=yyyy-mm-dd
|
||||
issues.due_date_form_add=마감일 추가
|
||||
issues.due_date_form_edit=편집
|
||||
@@ -790,8 +790,8 @@ issues.due_date_form_remove=삭제
|
||||
issues.due_date_not_set=마감일이 설정되지 않았습니다.
|
||||
issues.due_date_added=마감일 %s 를 추가 %s
|
||||
issues.due_date_remove=%s %s 마감일이 삭제되었습니다.
|
||||
issues.due_date_overdue="기한 초과"
|
||||
issues.due_date_invalid="기한이 올바르지 않거나 범위를 벗어났습니다. 'yyyy-mm-dd'형식을 사용해주십시오."
|
||||
issues.due_date_overdue=기한 초과
|
||||
issues.due_date_invalid=기한이 올바르지 않거나 범위를 벗어났습니다. 'yyyy-mm-dd'형식을 사용해주십시오.
|
||||
issues.dependency.title=의존성
|
||||
issues.dependency.add=의존성 추가...
|
||||
issues.dependency.cancel=취소
|
||||
@@ -809,7 +809,7 @@ issues.dependency.add_error_dep_exists=의존성이 이미 존재합니다.
|
||||
issues.dependency.add_error_dep_not_same_repo=두 이슈는 같은 레포지토리 안에 있어야 합니다.
|
||||
issues.review.self.approval=자신의 풀 리퀘스트를 승인할 수 없습니다.
|
||||
issues.review.self.rejection=자신의 풀 리퀘스트에 대한 변경을 요청할 수 없습니다.
|
||||
issues.review.approve="이 변경사항을 승인하였습니다. %s"
|
||||
issues.review.approve=이 변경사항을 승인하였습니다. %s
|
||||
issues.review.pending=보류
|
||||
issues.review.review=검토
|
||||
issues.review.reviewers=리뷰어
|
||||
@@ -824,7 +824,7 @@ pulls.compare_base=병합하기
|
||||
pulls.compare_compare=다음으로부터 풀
|
||||
pulls.filter_branch=Filter Branch
|
||||
pulls.create=풀 리퀘스트 생성
|
||||
pulls.title_desc="<code>%[2]s</code> 에서 <code id=\"branch_target\">%[3]s</code> 로 %[1]d commits 를 머지하려 합니다"
|
||||
pulls.title_desc=<code>%[2]s</code> 에서 <code id="branch_target">%[3]s</code> 로 %[1]d commits 를 머지하려 합니다
|
||||
pulls.merged_title_desc=<code>%[2]s</code> 에서 <code>%[3]s</code> 로 %[1]d commits 를 머지했습니다 %[4]s
|
||||
pulls.tab_conversation=대화
|
||||
pulls.tab_commits=커밋
|
||||
@@ -855,7 +855,7 @@ milestones.title=타이틀
|
||||
milestones.desc=설명
|
||||
milestones.due_date=기한 (선택 사항)
|
||||
milestones.clear=지우기
|
||||
milestones.invalid_due_date_format="마감일은 반드시 'yyyy-mm-dd' 형식이어야 합니다."
|
||||
milestones.invalid_due_date_format=마감일은 반드시 'yyyy-mm-dd' 형식이어야 합니다.
|
||||
milestones.edit=마일스톤 편집
|
||||
milestones.cancel=취소
|
||||
milestones.modify=마일스톤 갱신
|
||||
|
||||
@@ -495,7 +495,7 @@ register_notify.text_3=Se esta conta foi criada para si, <a href="%s">defina a s
|
||||
|
||||
reset_password=Recupere a sua conta
|
||||
reset_password.title=%s, você pediu para recuperar a sua conta
|
||||
reset_password.text=Por favor clique na seguinte ligação para recuperar a sua conta em <b>%s</b>:
|
||||
reset_password.text=Para recuperar a sua conta, clique na ligação seguinte (válida por <b>%s</b>):
|
||||
|
||||
register_success=Inscrição bem sucedida
|
||||
|
||||
@@ -1017,6 +1017,8 @@ owner=Proprietário(a)
|
||||
owner_helper=Algumas organizações podem não aparecer na lista suspensa devido a um limite máximo de contagem de repositórios.
|
||||
repo_name=Nome do repositório
|
||||
repo_name_profile_public_hint=.profile é um repositório especial que pode usar para adicionar README.md ao seu perfil público da organização, visível para qualquer pessoa. Certifique-se que é público e inicialize-o com um README na pasta do perfil para começar.
|
||||
repo_name_profile_private_hint=.profile-private é um repositório especial que pode usar para adicionar um README.md ao seu perfil de membro da organização, visível apenas para membros da organização. Certifique-se que é privado e inicialize-o com um README na pasta de perfil para começar.
|
||||
repo_name_helper=Bons nomes de repositórios usam palavras-chave curtas, memorizáveis e únicas. Um repositório chamado ".profile" ou ".profile-private" pode ser usado para adicionar um README.md ao perfil do utilizador ou da organização.
|
||||
repo_size=Tamanho do repositório
|
||||
template=Modelo
|
||||
template_select=Escolha um modelo.
|
||||
@@ -1233,6 +1235,7 @@ create_new_repo_command=Criando um novo repositório na linha de comandos
|
||||
push_exist_repo=Enviando, pela linha de comandos, um repositório existente
|
||||
empty_message=Este repositório não contém qualquer conteúdo.
|
||||
broken_message=Os dados Git subjacentes a este repositório não podem ser lidos. Contacte o administrador desta instância ou elimine este repositório.
|
||||
no_branch=Este repositório não tem quaisquer ramos.
|
||||
|
||||
code=Código
|
||||
code.desc=Aceder ao código fonte, ficheiros, cometimentos e ramos.
|
||||
@@ -2624,6 +2627,9 @@ diff.image.overlay=Sobrepor
|
||||
diff.has_escaped=Esta linha tem caracteres unicode escondidos
|
||||
diff.show_file_tree=Mostrar árvore de ficheiros
|
||||
diff.hide_file_tree=Esconder árvore de ficheiros
|
||||
diff.submodule_added=Submódulo %[1]s adicionado em %[2]s
|
||||
diff.submodule_deleted=Submódulo %[1]s eliminado de %[2]s
|
||||
diff.submodule_updated=Submódulo %[1]s modificado: %[2]s
|
||||
|
||||
releases.desc=Acompanhe as versões e as descargas do repositório.
|
||||
release.releases=Lançamentos
|
||||
@@ -3761,6 +3767,7 @@ workflow.not_found=A sequência de trabalho '%s' não foi encontrada.
|
||||
workflow.run_success=A sequência de trabalho '%s' foi executada com sucesso.
|
||||
workflow.from_ref=Usar sequência de trabalho de
|
||||
workflow.has_workflow_dispatch=Esta sequência de trabalho tem um despoletador de eventos workflow_dispatch.
|
||||
workflow.has_no_workflow_dispatch=A sequência de trabalho '%s' não tem nenhum despoletador de eventos workflow_dispatch.
|
||||
|
||||
need_approval_desc=É necessária aprovação para executar sequências de trabalho para a derivação do pedido de integração.
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
|
||||
pck, err := alpine_module.ParsePackage(buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) || err == io.EOF {
|
||||
if errors.Is(err, util.ErrInvalidArgument) || errors.Is(err, io.EOF) {
|
||||
apiError(ctx, http.StatusBadRequest, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
+18
-139
@@ -138,7 +138,8 @@ func CommonRoutes() *web.Router {
|
||||
}, reqPackageAccess(perm.AccessModeRead))
|
||||
r.Group("/arch", func() {
|
||||
r.Methods("HEAD,GET", "/repository.key", arch.GetRepositoryKey)
|
||||
r.PathGroup("*", func(g *web.RouterPathGroup) {
|
||||
r.Methods("PUT", "" /* no repository */, reqPackageAccess(perm.AccessModeWrite), arch.UploadPackageFile)
|
||||
r.PathGroup("/*", func(g *web.RouterPathGroup) {
|
||||
g.MatchPath("PUT", "/<repository:*>", reqPackageAccess(perm.AccessModeWrite), arch.UploadPackageFile)
|
||||
g.MatchPath("HEAD,GET", "/<repository:*>/<architecture>/<filename>", arch.GetPackageOrRepositoryFile)
|
||||
g.MatchPath("DELETE", "/<repository:*>/<name>/<version>/<architecture>", reqPackageAccess(perm.AccessModeWrite), arch.DeletePackageVersion)
|
||||
@@ -698,150 +699,28 @@ func ContainerRoutes() *web.Router {
|
||||
})
|
||||
r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
|
||||
r.Group("/{username}", func() {
|
||||
r.Group("/{image}", func() {
|
||||
r.Group("/blobs/uploads", func() {
|
||||
r.Post("", container.InitiateUploadBlob)
|
||||
r.Group("/{uuid}", func() {
|
||||
r.Get("", container.GetUploadBlob)
|
||||
r.Patch("", container.UploadBlob)
|
||||
r.Put("", container.EndUploadBlob)
|
||||
r.Delete("", container.CancelUploadBlob)
|
||||
})
|
||||
}, reqPackageAccess(perm.AccessModeWrite))
|
||||
r.Group("/blobs/{digest}", func() {
|
||||
r.Head("", container.HeadBlob)
|
||||
r.Get("", container.GetBlob)
|
||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
|
||||
})
|
||||
r.Group("/manifests/{reference}", func() {
|
||||
r.Put("", reqPackageAccess(perm.AccessModeWrite), container.UploadManifest)
|
||||
r.Head("", container.HeadManifest)
|
||||
r.Get("", container.GetManifest)
|
||||
r.Delete("", reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
|
||||
})
|
||||
r.Get("/tags/list", container.GetTagList)
|
||||
}, container.VerifyImageName)
|
||||
|
||||
var (
|
||||
blobsUploadsPattern = regexp.MustCompile(`\A(.+)/blobs/uploads/([a-zA-Z0-9-_.=]+)\z`)
|
||||
blobsPattern = regexp.MustCompile(`\A(.+)/blobs/([^/]+)\z`)
|
||||
manifestsPattern = regexp.MustCompile(`\A(.+)/manifests/([^/]+)\z`)
|
||||
)
|
||||
|
||||
// Manual mapping of routes because {image} can contain slashes which chi does not support
|
||||
r.Methods("HEAD,GET,POST,PUT,PATCH,DELETE", "/*", func(ctx *context.Context) {
|
||||
path := ctx.PathParam("*")
|
||||
isHead := ctx.Req.Method == "HEAD"
|
||||
isGet := ctx.Req.Method == "GET"
|
||||
isPost := ctx.Req.Method == "POST"
|
||||
isPut := ctx.Req.Method == "PUT"
|
||||
isPatch := ctx.Req.Method == "PATCH"
|
||||
isDelete := ctx.Req.Method == "DELETE"
|
||||
|
||||
if isPost && strings.HasSuffix(path, "/blobs/uploads") {
|
||||
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetPathParam("image", path[:len(path)-14])
|
||||
container.VerifyImageName(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
container.InitiateUploadBlob(ctx)
|
||||
return
|
||||
}
|
||||
if isGet && strings.HasSuffix(path, "/tags/list") {
|
||||
ctx.SetPathParam("image", path[:len(path)-10])
|
||||
container.VerifyImageName(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
container.GetTagList(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
m := blobsUploadsPattern.FindStringSubmatch(path)
|
||||
if len(m) == 3 && (isGet || isPut || isPatch || isDelete) {
|
||||
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetPathParam("image", m[1])
|
||||
container.VerifyImageName(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetPathParam("uuid", m[2])
|
||||
|
||||
if isGet {
|
||||
r.PathGroup("/*", func(g *web.RouterPathGroup) {
|
||||
g.MatchPath("POST", "/<image:*>/blobs/uploads", reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, container.InitiateUploadBlob)
|
||||
g.MatchPath("GET", "/<image:*>/tags/list", container.VerifyImageName, container.GetTagList)
|
||||
g.MatchPath("GET,PATCH,PUT,DELETE", `/<image:*>/blobs/uploads/<uuid:[-.=\w]+>`, reqPackageAccess(perm.AccessModeWrite), container.VerifyImageName, func(ctx *context.Context) {
|
||||
if ctx.Req.Method == http.MethodGet {
|
||||
container.GetUploadBlob(ctx)
|
||||
} else if isPatch {
|
||||
} else if ctx.Req.Method == http.MethodPatch {
|
||||
container.UploadBlob(ctx)
|
||||
} else if isPut {
|
||||
} else if ctx.Req.Method == http.MethodPut {
|
||||
container.EndUploadBlob(ctx)
|
||||
} else {
|
||||
} else /* DELETE */ {
|
||||
container.CancelUploadBlob(ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
m = blobsPattern.FindStringSubmatch(path)
|
||||
if len(m) == 3 && (isHead || isGet || isDelete) {
|
||||
ctx.SetPathParam("image", m[1])
|
||||
container.VerifyImageName(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
})
|
||||
g.MatchPath("HEAD", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.HeadBlob)
|
||||
g.MatchPath("GET", `/<image:*>/blobs/<digest>`, container.VerifyImageName, container.GetBlob)
|
||||
g.MatchPath("DELETE", `/<image:*>/blobs/<digest>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteBlob)
|
||||
|
||||
ctx.SetPathParam("digest", m[2])
|
||||
|
||||
if isHead {
|
||||
container.HeadBlob(ctx)
|
||||
} else if isGet {
|
||||
container.GetBlob(ctx)
|
||||
} else {
|
||||
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
container.DeleteBlob(ctx)
|
||||
}
|
||||
return
|
||||
}
|
||||
m = manifestsPattern.FindStringSubmatch(path)
|
||||
if len(m) == 3 && (isHead || isGet || isPut || isDelete) {
|
||||
ctx.SetPathParam("image", m[1])
|
||||
container.VerifyImageName(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetPathParam("reference", m[2])
|
||||
|
||||
if isHead {
|
||||
container.HeadManifest(ctx)
|
||||
} else if isGet {
|
||||
container.GetManifest(ctx)
|
||||
} else {
|
||||
reqPackageAccess(perm.AccessModeWrite)(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if isPut {
|
||||
container.UploadManifest(ctx)
|
||||
} else {
|
||||
container.DeleteManifest(ctx)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusNotFound)
|
||||
g.MatchPath("HEAD", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.HeadManifest)
|
||||
g.MatchPath("GET", `/<image:*>/manifests/<reference>`, container.VerifyImageName, container.GetManifest)
|
||||
g.MatchPath("PUT", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.UploadManifest)
|
||||
g.MatchPath("DELETE", `/<image:*>/manifests/<reference>`, container.VerifyImageName, reqPackageAccess(perm.AccessModeWrite), container.DeleteManifest)
|
||||
})
|
||||
}, container.ReqContainerAccess, context.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
|
||||
pck, err := arch_module.ParsePackage(buf)
|
||||
if err != nil {
|
||||
if errors.Is(err, util.ErrInvalidArgument) || err == io.EOF {
|
||||
if errors.Is(err, util.ErrInvalidArgument) || errors.Is(err, io.EOF) {
|
||||
apiError(ctx, http.StatusBadRequest, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -181,7 +181,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -276,7 +276,7 @@ func UnyankPackage(ctx *context.Context) {
|
||||
func yankPackage(ctx *context.Context, yank bool) {
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeCargo, ctx.PathParam("package"), ctx.PathParam("version"))
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ func PackageVersionMetadata(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeChef, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func UploadPackage(ctx *context.Context) {
|
||||
func DownloadPackage(ctx *context.Context) {
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeChef, ctx.PathParam("name"), ctx.PathParam("version"))
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -368,7 +368,7 @@ func DeletePackageVersion(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -176,7 +176,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ package conan
|
||||
|
||||
import (
|
||||
std_ctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -183,7 +184,7 @@ func serveSnapshot(ctx *context.Context, fileKey string) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -244,7 +245,7 @@ func serveDownloadURLs(ctx *context.Context, fileKey, downloadURL string) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -493,7 +494,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -509,7 +510,7 @@ func DeleteRecipeV1(ctx *context.Context) {
|
||||
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
|
||||
|
||||
if err := deleteRecipeOrPackage(ctx, rref, true, nil, false); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -524,7 +525,7 @@ func DeleteRecipeV2(ctx *context.Context) {
|
||||
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
|
||||
|
||||
if err := deleteRecipeOrPackage(ctx, rref, rref.Revision == "", nil, false); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -571,7 +572,7 @@ func DeletePackageV1(ctx *context.Context) {
|
||||
for _, reference := range references {
|
||||
pref, _ := conan_module.NewPackageReference(currentRref, reference.Value, conan_module.DefaultRevision)
|
||||
if err := deleteRecipeOrPackage(ctx, currentRref, true, pref, true); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -590,7 +591,7 @@ func DeletePackageV2(ctx *context.Context) {
|
||||
|
||||
if pref != nil { // has package reference
|
||||
if err := deleteRecipeOrPackage(ctx, rref, false, pref, pref.Revision == ""); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -615,7 +616,7 @@ func DeletePackageV2(ctx *context.Context) {
|
||||
pref, _ := conan_module.NewPackageReference(rref, reference.Value, conan_module.DefaultRevision)
|
||||
|
||||
if err := deleteRecipeOrPackage(ctx, rref, false, pref, true); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -749,7 +750,7 @@ func LatestRecipeRevision(ctx *context.Context) {
|
||||
|
||||
revision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrRecipeReferenceNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -766,7 +767,7 @@ func LatestPackageRevision(ctx *context.Context) {
|
||||
|
||||
revision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrRecipeReferenceNotExist || err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) || errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -796,7 +797,7 @@ func listRevisionFiles(ctx *context.Context, fileKey string) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeConan, rref.Name, rref.Version)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package conan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -76,7 +77,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
|
||||
if !searchAllRevisions && rref.Revision == "" {
|
||||
lastRevision, err := conan_model.GetLastRecipeRevision(ctx, ctx.Package.Owner.ID, rref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrRecipeReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -87,7 +88,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
|
||||
} else {
|
||||
has, err := conan_model.RecipeExists(ctx, ctx.Package.Owner.ID, rref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrRecipeReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -119,7 +120,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
|
||||
}
|
||||
packageReferences, err := conan_model.GetPackageReferences(ctx, ctx.Package.Owner.ID, currentRef)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrRecipeReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrRecipeReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -133,7 +134,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
|
||||
pref, _ := conan_module.NewPackageReference(currentRef, packageReference.Value, "")
|
||||
lastPackageRevision, err := conan_model.GetLastPackageRevision(ctx, ctx.Package.Owner.ID, pref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -143,7 +144,7 @@ func searchPackages(ctx *context.Context, searchAllRevisions bool) {
|
||||
pref = pref.WithRevision(lastPackageRevision.Value)
|
||||
infoRaw, err := conan_model.GetPackageInfo(ctx, ctx.Package.Owner.ID, pref)
|
||||
if err != nil {
|
||||
if err == conan_model.ErrPackageReferenceNotExist {
|
||||
if errors.Is(err, conan_model.ErrPackageReferenceNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -111,12 +111,11 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
|
||||
}
|
||||
var err error
|
||||
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
|
||||
if err == packages_model.ErrDuplicatePackage {
|
||||
created = false
|
||||
} else {
|
||||
if !errors.Is(err, packages_model.ErrDuplicatePackage) {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return err
|
||||
}
|
||||
created = false
|
||||
}
|
||||
|
||||
if created {
|
||||
@@ -135,7 +134,7 @@ func getOrCreateUploadVersion(ctx context.Context, pi *packages_service.PackageI
|
||||
MetadataJSON: "null",
|
||||
}
|
||||
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
|
||||
if err != packages_model.ErrDuplicatePackageVersion {
|
||||
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return err
|
||||
}
|
||||
@@ -161,7 +160,7 @@ func createFileForBlob(ctx context.Context, pv *packages_model.PackageVersion, p
|
||||
}
|
||||
var err error
|
||||
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
|
||||
if err == packages_model.ErrDuplicatePackageFile {
|
||||
if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
|
||||
return nil
|
||||
}
|
||||
log.Error("Error inserting package file: %v", err)
|
||||
|
||||
@@ -324,7 +324,7 @@ func GetUploadBlob(ctx *context.Context) {
|
||||
|
||||
upload, err := packages_model.GetBlobUploadByID(ctx, uuid)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageBlobUploadNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUploadUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -345,7 +345,7 @@ func UploadBlob(ctx *context.Context) {
|
||||
|
||||
uploader, err := container_service.NewBlobUploader(ctx, ctx.PathParam("uuid"))
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageBlobUploadNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUploadUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -396,7 +396,7 @@ func EndUploadBlob(ctx *context.Context) {
|
||||
|
||||
uploader, err := container_service.NewBlobUploader(ctx, ctx.PathParam("uuid"))
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageBlobUploadNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUploadUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -465,7 +465,7 @@ func CancelUploadBlob(ctx *context.Context) {
|
||||
|
||||
_, err := packages_model.GetBlobUploadByID(ctx, uuid)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageBlobUploadNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageBlobUploadNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUploadUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -501,7 +501,7 @@ func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescri
|
||||
func HeadBlob(ctx *context.Context) {
|
||||
blob, err := getBlobFromContext(ctx)
|
||||
if err != nil {
|
||||
if err == container_model.ErrContainerBlobNotExist {
|
||||
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -520,7 +520,7 @@ func HeadBlob(ctx *context.Context) {
|
||||
func GetBlob(ctx *context.Context) {
|
||||
blob, err := getBlobFromContext(ctx)
|
||||
if err != nil {
|
||||
if err == container_model.ErrContainerBlobNotExist {
|
||||
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
|
||||
apiErrorDefined(ctx, errBlobUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -639,7 +639,7 @@ func getManifestFromContext(ctx *context.Context) (*packages_model.PackageFileDe
|
||||
func HeadManifest(ctx *context.Context) {
|
||||
manifest, err := getManifestFromContext(ctx)
|
||||
if err != nil {
|
||||
if err == container_model.ErrContainerBlobNotExist {
|
||||
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
|
||||
apiErrorDefined(ctx, errManifestUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -659,7 +659,7 @@ func HeadManifest(ctx *context.Context) {
|
||||
func GetManifest(ctx *context.Context) {
|
||||
manifest, err := getManifestFromContext(ctx)
|
||||
if err != nil {
|
||||
if err == container_model.ErrContainerBlobNotExist {
|
||||
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
|
||||
apiErrorDefined(ctx, errManifestUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -739,7 +739,7 @@ func GetTagList(ctx *context.Context) {
|
||||
image := ctx.PathParam("image")
|
||||
|
||||
if _, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.TypeContainer, image); err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiErrorDefined(ctx, errNameUnknown)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -240,7 +240,7 @@ func processImageManifestIndex(ctx context.Context, mci *manifestCreationInfo, b
|
||||
IsManifest: true,
|
||||
})
|
||||
if err != nil {
|
||||
if err == container_model.ErrContainerBlobNotExist {
|
||||
if errors.Is(err, container_model.ErrContainerBlobNotExist) {
|
||||
return errManifestBlobUnknown
|
||||
}
|
||||
return err
|
||||
@@ -321,12 +321,11 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
|
||||
}
|
||||
var err error
|
||||
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
|
||||
if err == packages_model.ErrDuplicatePackage {
|
||||
created = false
|
||||
} else {
|
||||
if !errors.Is(err, packages_model.ErrDuplicatePackage) {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
created = false
|
||||
}
|
||||
|
||||
if created {
|
||||
@@ -352,21 +351,23 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
|
||||
}
|
||||
var pv *packages_model.PackageVersion
|
||||
if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
|
||||
if err == packages_model.ErrDuplicatePackageVersion {
|
||||
if err := packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// keep download count on overwrite
|
||||
_pv.DownloadCount = pv.DownloadCount
|
||||
if err = packages_service.DeletePackageVersionAndReferences(ctx, pv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
|
||||
// keep download count on overwrite
|
||||
_pv.DownloadCount = pv.DownloadCount
|
||||
|
||||
if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
|
||||
if !errors.Is(err, packages_model.ErrDuplicatePackageVersion) {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
log.Error("Error inserting package: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,7 +418,7 @@ func createFileFromBlobReference(ctx context.Context, pv, uploadVersion *package
|
||||
}
|
||||
var err error
|
||||
if pf, err = packages_model.TryInsertFile(ctx, pf); err != nil {
|
||||
if err == packages_model.ErrDuplicatePackageFile {
|
||||
if errors.Is(err, packages_model.ErrDuplicatePackageFile) {
|
||||
// Skip this blob because the manifest contains the same filesystem layer multiple times.
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
} else {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
|
||||
@@ -44,7 +44,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -155,7 +155,7 @@ func DeletePackage(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -182,7 +182,7 @@ func DeletePackageFile(ctx *context.Context) {
|
||||
return pv, pf, nil
|
||||
}()
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
|
||||
BundleDependencies: metadata.BundleDependencies,
|
||||
DevDependencies: metadata.DevelopmentDependencies,
|
||||
PeerDependencies: metadata.PeerDependencies,
|
||||
PeerDependenciesMeta: metadata.PeerDependenciesMeta,
|
||||
OptionalDependencies: metadata.OptionalDependencies,
|
||||
Readme: metadata.Readme,
|
||||
Bin: metadata.Bin,
|
||||
|
||||
@@ -98,7 +98,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -140,7 +140,7 @@ func DownloadPackageFileByName(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func UploadPackage(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL)
|
||||
repo, err := repo_model.GetRepositoryByURLRelax(ctx, npmPackage.Metadata.Repository.URL)
|
||||
if err == nil {
|
||||
canWrite := repo.OwnerID == ctx.Doer.ID
|
||||
|
||||
@@ -267,7 +267,7 @@ func DeletePackageVersion(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -341,7 +341,7 @@ func AddPackageTag(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName, version)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -259,7 +259,7 @@ func RegistrationLeafV2(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -288,7 +288,7 @@ func RegistrationLeafV3(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -671,7 +671,7 @@ func DownloadSymbolFile(ctx *context.Context) {
|
||||
|
||||
s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -699,7 +699,7 @@ func DeletePackage(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ func PackageVersionMetadata(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -233,7 +233,7 @@ func FinalizePackage(ctx *context.Context) {
|
||||
|
||||
_, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -258,7 +258,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
|
||||
pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ package pypi
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
@@ -93,7 +95,7 @@ func DownloadPackageFile(ctx *context.Context) {
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
|
||||
if errors.Is(err, packages_model.ErrPackageNotExist) || errors.Is(err, packages_model.ErrPackageFileNotExist) {
|
||||
apiError(ctx, http.StatusNotFound, err)
|
||||
return
|
||||
}
|
||||
@@ -139,9 +141,30 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
projectURL := ctx.Req.FormValue("home_page")
|
||||
if !validation.IsValidURL(projectURL) {
|
||||
projectURL = ""
|
||||
// Ensure ctx.Req.Form exists.
|
||||
_ = ctx.Req.ParseForm()
|
||||
|
||||
var homepageURL string
|
||||
projectURLs := ctx.Req.Form["project_urls"]
|
||||
for _, purl := range projectURLs {
|
||||
label, url, found := strings.Cut(purl, ",")
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
if normalizeLabel(label) != "homepage" {
|
||||
continue
|
||||
}
|
||||
homepageURL = strings.TrimSpace(url)
|
||||
break
|
||||
}
|
||||
|
||||
if len(homepageURL) == 0 {
|
||||
// TODO: Home-page is a deprecated metadata field. Remove this branch once it's no longer apart of the spec.
|
||||
homepageURL = ctx.Req.FormValue("home_page")
|
||||
}
|
||||
|
||||
if !validation.IsValidURL(homepageURL) {
|
||||
homepageURL = ""
|
||||
}
|
||||
|
||||
_, _, err = packages_service.CreatePackageOrAddFileToExisting(
|
||||
@@ -160,7 +183,7 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
Description: ctx.Req.FormValue("description"),
|
||||
LongDescription: ctx.Req.FormValue("long_description"),
|
||||
Summary: ctx.Req.FormValue("summary"),
|
||||
ProjectURL: projectURL,
|
||||
ProjectURL: homepageURL,
|
||||
License: ctx.Req.FormValue("license"),
|
||||
RequiresPython: ctx.Req.FormValue("requires_python"),
|
||||
},
|
||||
@@ -189,6 +212,23 @@ func UploadPackageFile(ctx *context.Context) {
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
// Normalizes a Project-URL label.
|
||||
// See https://packaging.python.org/en/latest/specifications/well-known-project-urls/#label-normalization.
|
||||
func normalizeLabel(label string) string {
|
||||
var builder strings.Builder
|
||||
|
||||
// "A label is normalized by deleting all ASCII punctuation and whitespace, and then converting the result
|
||||
// to lowercase."
|
||||
for _, r := range label {
|
||||
if unicode.IsPunct(r) || unicode.IsSpace(r) {
|
||||
continue
|
||||
}
|
||||
builder.WriteRune(unicode.ToLower(r))
|
||||
}
|
||||
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func isValidNameAndVersion(packageName, packageVersion string) bool {
|
||||
return nameMatcher.MatchString(packageName) && versionMatcher.MatchString(packageVersion)
|
||||
}
|
||||
Loaded 100 of 211 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user