Compare commits
51 Commits
c6a89e7be1
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cbe4505d1e | ||
|
|
97b6db5b31 | ||
|
|
46b2bc0d8e | ||
|
|
77ee51c606 | ||
|
|
e0aedae6e0 | ||
|
|
4d0b237127 | ||
|
|
d098ffd3d0 | ||
|
|
4eb58fc8ad | ||
|
|
40f8a546f5 | ||
|
|
5dd481a81c | ||
|
|
8b4410eee7 | ||
|
|
bacea404e2 | ||
|
|
5e59ea319f | ||
|
|
5f784270e2 | ||
|
|
97ee27fe79 | ||
|
|
482a586b6e | ||
|
|
f2136260c9 | ||
|
|
54bf61d301 | ||
|
|
89f92d265a | ||
|
|
d50ee1a708 | ||
|
|
e1355d68b4 | ||
|
|
7f6ca3c18a | ||
|
|
546ebf87bb | ||
|
|
5fef1797a7 | ||
|
|
e354e20f2d | ||
|
|
3bb88d104e | ||
|
|
ad89cc5d59 | ||
|
|
281c9c2e4f | ||
|
|
07c765637c | ||
|
|
95772673f6 | ||
|
|
8ee140ae13 | ||
|
|
74ca178a8b | ||
|
|
c3d70f8dc5 | ||
|
|
99ec1ae40a | ||
|
|
18758407c7 | ||
|
|
ba79ecd029 | ||
|
|
5ae6f97c69 | ||
|
|
10310ed874 | ||
|
|
a33c5e8f92 | ||
|
|
10255953ee | ||
|
|
757a0e51a9 | ||
|
|
7d03ff3bd3 | ||
|
|
5fe73247b0 | ||
|
|
081e9bc86d | ||
|
|
b473cf87e1 | ||
|
|
224344339e | ||
|
|
b5bf43a6d5 | ||
|
|
32bd17f2d5 | ||
|
|
a1fd986012 | ||
|
|
22cc06b543 | ||
|
|
e6d3a90231 |
14
.gitignore
vendored
@@ -1,5 +1,13 @@
|
||||
vendor
|
||||
repo
|
||||
repo/*
|
||||
db/*.sqlite3
|
||||
projects
|
||||
.ruby-lsp
|
||||
projects/*
|
||||
.ruby-lsp
|
||||
keys/private
|
||||
keys/public
|
||||
keys/save
|
||||
logs/actions.log
|
||||
|
||||
!.gitignore
|
||||
!projects/empty
|
||||
!repo/empty
|
||||
6
Gemfile
@@ -17,9 +17,7 @@ gem "inifile", "~> 3.0"
|
||||
|
||||
gem "carrierwave", "~> 3.1"
|
||||
|
||||
gem "data_mapper", "~> 1.2"
|
||||
|
||||
gem "json", "~> 1.8"
|
||||
gem "json", "~> 2.5"
|
||||
|
||||
gem "stringio", "~> 3.1"
|
||||
|
||||
@@ -36,3 +34,5 @@ gem "sequel", "~> 5.89"
|
||||
gem "ffi", "~> 1.17"
|
||||
|
||||
gem "ptools", "~> 1.5"
|
||||
|
||||
gem "ostruct", "~> 0.6.1"
|
||||
|
||||
156
Gemfile.lock
@@ -1,16 +1,16 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
activemodel (8.0.1)
|
||||
activesupport (= 8.0.1)
|
||||
activesupport (8.0.1)
|
||||
activemodel (8.1.1)
|
||||
activesupport (= 8.1.1)
|
||||
activesupport (8.1.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
json
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
securerandom (>= 0.3)
|
||||
@@ -18,13 +18,9 @@ GEM
|
||||
uri (>= 0.13.1)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
base64 (0.2.0)
|
||||
bcrypt (3.1.20)
|
||||
bcrypt-ruby (3.1.5)
|
||||
bcrypt (>= 3.1.3)
|
||||
benchmark (0.4.0)
|
||||
bigdecimal (3.1.9)
|
||||
carrierwave (3.1.1)
|
||||
base64 (0.3.0)
|
||||
bigdecimal (3.3.1)
|
||||
carrierwave (3.1.2)
|
||||
activemodel (>= 6.0.0)
|
||||
activesupport (>= 6.0.0)
|
||||
addressable (~> 2.6)
|
||||
@@ -32,129 +28,82 @@ GEM
|
||||
marcel (~> 1.0.0)
|
||||
ssrf_filter (~> 1.0)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.0)
|
||||
data_mapper (1.2.0)
|
||||
dm-aggregates (~> 1.2.0)
|
||||
dm-constraints (~> 1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-migrations (~> 1.2.0)
|
||||
dm-serializer (~> 1.2.0)
|
||||
dm-timestamps (~> 1.2.0)
|
||||
dm-transactions (~> 1.2.0)
|
||||
dm-types (~> 1.2.0)
|
||||
dm-validations (~> 1.2.0)
|
||||
dm-aggregates (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-constraints (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-core (1.2.1)
|
||||
addressable (~> 2.3)
|
||||
dm-migrations (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-serializer (1.2.2)
|
||||
dm-core (~> 1.2.0)
|
||||
fastercsv (~> 1.5)
|
||||
json (~> 1.6)
|
||||
json_pure (~> 1.6)
|
||||
multi_json (~> 1.0)
|
||||
dm-timestamps (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-transactions (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
dm-types (1.2.2)
|
||||
bcrypt-ruby (~> 3.0)
|
||||
dm-core (~> 1.2.0)
|
||||
fastercsv (~> 1.5)
|
||||
json (~> 1.6)
|
||||
multi_json (~> 1.0)
|
||||
stringex (~> 1.4)
|
||||
uuidtools (~> 2.1)
|
||||
dm-validations (1.2.0)
|
||||
dm-core (~> 1.2.0)
|
||||
drb (2.2.1)
|
||||
fastercsv (1.5.5)
|
||||
ffi (1.17.1)
|
||||
ffi (1.17.1-aarch64-linux-gnu)
|
||||
ffi (1.17.1-aarch64-linux-musl)
|
||||
ffi (1.17.1-arm-linux-gnu)
|
||||
ffi (1.17.1-arm-linux-musl)
|
||||
ffi (1.17.1-arm64-darwin)
|
||||
ffi (1.17.1-x86-linux-gnu)
|
||||
ffi (1.17.1-x86-linux-musl)
|
||||
ffi (1.17.1-x86_64-darwin)
|
||||
ffi (1.17.1-x86_64-linux-gnu)
|
||||
ffi (1.17.1-x86_64-linux-musl)
|
||||
connection_pool (2.5.4)
|
||||
drb (2.2.3)
|
||||
ffi (1.17.2-aarch64-linux-gnu)
|
||||
ffi (1.17.2-aarch64-linux-musl)
|
||||
ffi (1.17.2-arm-linux-gnu)
|
||||
ffi (1.17.2-arm-linux-musl)
|
||||
ffi (1.17.2-arm64-darwin)
|
||||
ffi (1.17.2-x86-linux-gnu)
|
||||
ffi (1.17.2-x86-linux-musl)
|
||||
ffi (1.17.2-x86_64-darwin)
|
||||
ffi (1.17.2-x86_64-linux-gnu)
|
||||
ffi (1.17.2-x86_64-linux-musl)
|
||||
i18n (1.14.7)
|
||||
concurrent-ruby (~> 1.0)
|
||||
image_processing (1.14.0)
|
||||
mini_magick (>= 4.9.5, < 6)
|
||||
ruby-vips (>= 2.0.17, < 3)
|
||||
inifile (3.0.0)
|
||||
json (1.8.6)
|
||||
json_pure (1.8.6)
|
||||
logger (1.6.6)
|
||||
json (2.15.2)
|
||||
logger (1.7.0)
|
||||
marcel (1.0.4)
|
||||
mini_magick (5.1.2)
|
||||
benchmark
|
||||
mini_magick (5.3.1)
|
||||
logger
|
||||
mini_portile2 (2.8.8)
|
||||
minitest (5.25.4)
|
||||
multi_json (1.15.0)
|
||||
mustermann (3.0.3)
|
||||
minitest (5.26.0)
|
||||
mustermann (3.0.4)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
nio4r (2.7.4)
|
||||
nio4r (2.7.5)
|
||||
ostruct (0.6.3)
|
||||
ptools (1.5.0)
|
||||
public_suffix (6.0.1)
|
||||
puma (6.6.0)
|
||||
public_suffix (6.0.2)
|
||||
puma (6.6.1)
|
||||
nio4r (~> 2.0)
|
||||
rack (3.1.10)
|
||||
rack-protection (4.1.1)
|
||||
rack (3.2.4)
|
||||
rack-protection (4.2.1)
|
||||
base64 (>= 0.1.0)
|
||||
logger (>= 1.6.0)
|
||||
rack (>= 3.0.0, < 4)
|
||||
rack-session (2.1.0)
|
||||
rack-session (2.1.1)
|
||||
base64 (>= 0.1.0)
|
||||
rack (>= 3.0.0)
|
||||
rackup (2.2.1)
|
||||
rack (>= 3)
|
||||
ruby-vips (2.2.3)
|
||||
ruby-vips (2.2.5)
|
||||
ffi (~> 1.12)
|
||||
logger
|
||||
ruby2_keywords (0.0.5)
|
||||
rugged (1.9.0)
|
||||
securerandom (0.4.1)
|
||||
sequel (5.89.0)
|
||||
sequel (5.98.0)
|
||||
bigdecimal
|
||||
shotgun (0.9.2)
|
||||
rack (>= 1.0)
|
||||
sinatra (4.1.1)
|
||||
sinatra (4.2.1)
|
||||
logger (>= 1.6.0)
|
||||
mustermann (~> 3.0)
|
||||
rack (>= 3.0.0, < 4)
|
||||
rack-protection (= 4.1.1)
|
||||
rack-protection (= 4.2.1)
|
||||
rack-session (>= 2.0.0, < 3)
|
||||
tilt (~> 2.0)
|
||||
sqlite (1.0.2)
|
||||
sqlite3 (2.5.0)
|
||||
mini_portile2 (~> 2.8.0)
|
||||
sqlite3 (2.5.0-aarch64-linux-gnu)
|
||||
sqlite3 (2.5.0-aarch64-linux-musl)
|
||||
sqlite3 (2.5.0-arm-linux-gnu)
|
||||
sqlite3 (2.5.0-arm-linux-musl)
|
||||
sqlite3 (2.5.0-arm64-darwin)
|
||||
sqlite3 (2.5.0-x86-linux-gnu)
|
||||
sqlite3 (2.5.0-x86-linux-musl)
|
||||
sqlite3 (2.5.0-x86_64-darwin)
|
||||
sqlite3 (2.5.0-x86_64-linux-gnu)
|
||||
sqlite3 (2.5.0-x86_64-linux-musl)
|
||||
ssrf_filter (1.2.0)
|
||||
stringex (1.5.1)
|
||||
stringio (3.1.3)
|
||||
tilt (2.6.0)
|
||||
sqlite3 (2.7.4-aarch64-linux-gnu)
|
||||
sqlite3 (2.7.4-aarch64-linux-musl)
|
||||
sqlite3 (2.7.4-arm-linux-gnu)
|
||||
sqlite3 (2.7.4-arm-linux-musl)
|
||||
sqlite3 (2.7.4-arm64-darwin)
|
||||
sqlite3 (2.7.4-x86-linux-gnu)
|
||||
sqlite3 (2.7.4-x86-linux-musl)
|
||||
sqlite3 (2.7.4-x86_64-darwin)
|
||||
sqlite3 (2.7.4-x86_64-linux-gnu)
|
||||
sqlite3 (2.7.4-x86_64-linux-musl)
|
||||
ssrf_filter (1.3.0)
|
||||
stringio (3.1.7)
|
||||
tilt (2.6.1)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uri (1.0.2)
|
||||
uuidtools (2.2.0)
|
||||
uri (1.1.1)
|
||||
|
||||
PLATFORMS
|
||||
aarch64-linux-gnu
|
||||
@@ -162,7 +111,6 @@ PLATFORMS
|
||||
arm-linux-gnu
|
||||
arm-linux-musl
|
||||
arm64-darwin
|
||||
ruby
|
||||
x86-linux-gnu
|
||||
x86-linux-musl
|
||||
x86_64-darwin
|
||||
@@ -171,10 +119,10 @@ PLATFORMS
|
||||
|
||||
DEPENDENCIES
|
||||
carrierwave (~> 3.1)
|
||||
data_mapper (~> 1.2)
|
||||
ffi (~> 1.17)
|
||||
inifile (~> 3.0)
|
||||
json (~> 1.8)
|
||||
json (~> 2.5)
|
||||
ostruct (~> 0.6.1)
|
||||
ptools (~> 1.5)
|
||||
puma (~> 6.6)
|
||||
rackup (~> 2.2)
|
||||
@@ -187,4 +135,4 @@ DEPENDENCIES
|
||||
stringio (~> 3.1)
|
||||
|
||||
BUNDLED WITH
|
||||
2.6.3
|
||||
2.7.2
|
||||
|
||||
129
LICENSE
@@ -1,117 +1,28 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
Copyright (c) 2025 Alexey Berezhok
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
Preamble
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
|
||||
Copyright (c) 2025 Alexey Berezhok
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
|
||||
Данная лицензия разрешает лицам, получившим копию данного программного обеспечения и сопутствующей документации (далее — Программное обеспечение), безвозмездно использовать Программное обеспечение без ограничений, включая неограниченное право на использование, копирование, изменение, слияние, публикацию, распространение, сублицензирование и/или продажу копий Программного обеспечения, а также лицам, которым предоставляется данное Программное обеспечение, при соблюдении следующих условий:
|
||||
|
||||
For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
|
||||
Указанное выше уведомление об авторском праве и данные условия должны быть включены во все копии или значимые части данного Программного обеспечения.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and modification follow.
|
||||
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
one line to give the program's name and an idea of what it does. Copyright (C) yyyy name of author
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
signature of Ty Coon, 1 April 1989 Ty Coon, President of Vice
|
||||
ДАННОЕ ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНО ВЫРАЖЕННЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ ГАРАНТИИ ТОВАРНОЙ ПРИГОДНОСТИ, СООТВЕТСТВИЯ ПО ЕГО КОНКРЕТНОМУ НАЗНАЧЕНИЮ И ОТСУТСТВИЯ НАРУШЕНИЙ, НО НЕ ОГРАНИЧИВАЯСЬ ИМИ. НИ В КАКОМ СЛУЧАЕ АВТОРЫ ИЛИ ПРАВООБЛАДАТЕЛИ НЕ НЕСУТ ОТВЕТСТВЕННОСТИ ПО КАКИМ-ЛИБО ИСКАМ, ЗА УЩЕРБ ИЛИ ПО ИНЫМ ТРЕБОВАНИЯМ, В ТОМ ЧИСЛЕ, ПРИ ДЕЙСТВИИ КОНТРАКТА, ДЕЛИКТЕ ИЛИ ИНОЙ СИТУАЦИИ, ВОЗНИКШИМ ИЗ-ЗА ИСПОЛЬЗОВАНИЯ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ИЛИ ИНЫХ ДЕЙСТВИЙ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ.
|
||||
|
||||
152
README.md
@@ -1,11 +1,155 @@
|
||||
## Запуск
|
||||
# MockGUI
|
||||
|
||||
Обертка над mock для организации графического интерфейса сборки RPM пакетов
|
||||
|
||||

|
||||
|
||||
## Где можно запустить
|
||||
|
||||
ПО может быть установлено на следующих ОС:
|
||||
|
||||
* MSVSphere 9
|
||||
* Almalinux 9
|
||||
* RockyLinux 9
|
||||
* Centos 9 Stream
|
||||
|
||||
Требования к системе:
|
||||
|
||||
| | Минимальные требования |
|
||||
|--------|--------------------------------------------------------------|
|
||||
| CPU | 1 ядро |
|
||||
| Memory | Зависит от собираемых исходников, для небольших проектов 2Гб |
|
||||
| HDD | размер исходных кодов * 4 |
|
||||
|
||||
## Установка
|
||||
|
||||
### ansible + root
|
||||
|
||||
```
|
||||
bundle exec ruby app.rb
|
||||
dnf install epel-release
|
||||
dnf install ansible git
|
||||
git clone https://dev.brepo.ru/brepo/mock-gui.git
|
||||
cd mock-gui/install
|
||||
ansible-galaxy install -r requirements.yml
|
||||
ansible-playbook mock-gui-install.yml --ask-become-pass
|
||||
перезагрузить систему
|
||||
systemctl enable mockgui
|
||||
systemctl start mockgui
|
||||
зайти под пользователем mockgui в каталог /home/mockgui/mock-gui/gen-scripts и выполнить команду:
|
||||
./install-key UserName UserEmail 316224000 StrongSignPassword
|
||||
```
|
||||
|
||||
## Подготовка базы данных
|
||||
где UserName - имя пользователя ключа, UserEmail - email пользователя ключа, 316224000 - время действия ключа в секундах, StrongSignPassword - пароль для ключа
|
||||
|
||||
### ansible + пользователь в sudo
|
||||
|
||||
```
|
||||
bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3
|
||||
sudo dnf install epel-release
|
||||
sudo dnf install ansible git
|
||||
git clone https://dev.brepo.ru/brepo/mock-gui.git
|
||||
cd mock-gui/install
|
||||
ansible-galaxy install -r requirements.yml
|
||||
ansible-playbook mock-gui-install.yml --ask-become-pass
|
||||
перезагрузить систему
|
||||
sudo systemctl enable mockgui
|
||||
sudo systemctl start mockgui
|
||||
зайти под пользователем mockgui в каталог /home/mockgui/mock-gui/gen-scripts и выполнить команду:
|
||||
./install-key UserName UserEmail 316224000 StrongSignPassword
|
||||
```
|
||||
|
||||
где UserName - имя пользователя ключа, UserEmail - email пользователя ключа, 316224000 - время действия ключа в секундах, StrongSignPassword - пароль для ключа
|
||||
|
||||
### Ручная установка
|
||||
|
||||
Команды ниже выполнять под root или привилегированным пользователем с sudo:
|
||||
|
||||
1. отключить selinux
|
||||
2. `systemctl stop firewalld`
|
||||
3. `systemctl disable firewalld`
|
||||
4. `systemctl stop nftables`
|
||||
5. `systemctl disable nftables`
|
||||
6. `useradd mockgui`
|
||||
7.
|
||||
Добавить репозиторий:
|
||||
```
|
||||
# cat /etc/yum.repos.d/brepo_projects.repo
|
||||
[brepo_projects]
|
||||
name=msvsphere9 repo on repo.brepo.ru
|
||||
baseurl=https://repo.brepo.ru/hestia/
|
||||
enabled=1
|
||||
gpgkey=https://repo.brepo.ru/hestia/brepo_projects-gpg-key
|
||||
gpgcheck=1
|
||||
```
|
||||
Далее:
|
||||
|
||||
8. `dnf install epel-release`
|
||||
9. `dnf install mock rpmdevtools rpm-build ccache rpm-sign sqlite sqlite-devel alt-brepo-ruby33 openssh-server git tar gcc gcc-c++ make cmake alt-brepo-ruby33-devel openssl-devel zlib-devel`
|
||||
10. `usermod -a -G mock mockgui`
|
||||
11.
|
||||
добавить в .bashrc root и mockgui строки:
|
||||
`export PATH=/usr/lib64/ccache:$PATH`
|
||||
|
||||
Команды ниже выполнять под пользователем mockgui:
|
||||
|
||||
12. `cd ~`
|
||||
13. `git clone https://dev.brepo.ru/brepo/mock-gui.git`
|
||||
14. `cd mock-gui`
|
||||
15. `/opt/brepo/ruby33/bin/bundle install`
|
||||
16. `/opt/brepo/ruby33/bin/bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3`
|
||||
17. `cd gen-scripts`
|
||||
18. `./install-key UserName UserEmail 316224000 StrongSignPassword`
|
||||
|
||||
где UserName - имя пользователя ключа, UserEmail - email пользователя ключа, 316224000 - время действия ключа в секундах, StrongSignPassword - пароль для ключа
|
||||
|
||||
Следующая команда от root:
|
||||
|
||||
15. `cp /home/mockgui/mock-gui/mockgui.service /etc/systemd/system/mockgui.service`
|
||||
16. `systemctl enable mockgui.service --now`
|
||||
|
||||
|
||||
## Запуск без сервиса
|
||||
|
||||
Подготовка базы данных(делается один раз):
|
||||
|
||||
```
|
||||
/opt/brepo/ruby33/bin/bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3
|
||||
```
|
||||
|
||||
Запуск приложения
|
||||
|
||||
```
|
||||
/opt/brepo/ruby33/bin/bundle exec /opt/brepo/ruby33/bin/ruby app.rb
|
||||
```
|
||||
|
||||
## Использование приложения
|
||||
|
||||
Открыть в браузере страницу:
|
||||
|
||||
```
|
||||
http://[ip]:8081
|
||||
```
|
||||
|
||||
## Документация
|
||||
|
||||
[OnLine](https://docs.brepo.ru/mockgui/)
|
||||
|
||||
### Сборка локальной докумнтации
|
||||
|
||||
```
|
||||
cd docs/mock-gui
|
||||
pip install mkdocs
|
||||
pip install markupsafe==2.0.1
|
||||
pip install mkdocs-bootstrap
|
||||
```
|
||||
|
||||
Локальный сервер:
|
||||
|
||||
```
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
Или статическая документация:
|
||||
|
||||
```
|
||||
mkdocs build
|
||||
```
|
||||
20
VERSION
Normal file
@@ -0,0 +1,20 @@
|
||||
0.4-3
|
||||
* Исправлена ошибка при обнаружении некорректного Changelog
|
||||
|
||||
0.4-2
|
||||
* Добавлена возможность создания снимков неподписанного репозитория
|
||||
* Добавлена возможность управлять снимками репозитория
|
||||
* Добавлена возможность очистки старых пакетов репозитория
|
||||
* Добавленана возможность очищать закешированные билдруты
|
||||
* Окружение сборке по умолчанию переключено на AlmaLinux
|
||||
|
||||
0.3-1
|
||||
* Добавлена возможность добавлять сборки в состоянии ожидания
|
||||
* Изменена сортировка пакетов в repoview
|
||||
|
||||
0.2-1
|
||||
* Добавлена поддержка проектов для тестовых сборок (версия меняется каждую сборку)
|
||||
* Доработан вывод repoview
|
||||
|
||||
0.1-1
|
||||
* Релиз сборочной системы для rpm пакетов
|
||||
585
app.rb
@@ -19,6 +19,16 @@ require_relative "classes/utilities"
|
||||
require_relative "classes/projects"
|
||||
require_relative "classes/configs"
|
||||
|
||||
configure do
|
||||
log_file = File.new("logs/actions.log", "a+")
|
||||
|
||||
STDOUT.reopen(log_file)
|
||||
STDERR.reopen(log_file)
|
||||
|
||||
STDOUT.sync = true
|
||||
STDERR.sync = true
|
||||
end
|
||||
|
||||
def print_error_page(error_status, error_meaasge)
|
||||
@page_name = "Ошибка выполнения"
|
||||
@status_err = error_status
|
||||
@@ -28,7 +38,9 @@ end
|
||||
|
||||
cfg = IniConfig.new()
|
||||
db = DBase.new(cfg)
|
||||
db.cancel_hang_builds
|
||||
|
||||
set :bind, "0.0.0.0"
|
||||
set :port, cfg.get_port
|
||||
set :public_folder, File.dirname(__FILE__) + "/public"
|
||||
use Rack::Session::Pool, :expire_after => 2592000
|
||||
@@ -332,6 +344,7 @@ get "/prjcreate" do
|
||||
@local_list = result[:local]
|
||||
@old_nopublic = session[:prj_old_nopublic]
|
||||
@error_data = session[:prjcreate_error]
|
||||
@old_tmpbld = session[:prj_old_tmpbld]
|
||||
session[:prjcreate_error] = nil
|
||||
erb :prjcrt
|
||||
end
|
||||
@@ -341,12 +354,13 @@ post "/prjcreate" do
|
||||
session[:prj_old_description] = params["description"]
|
||||
session[:prj_old_list] = params["conflist"]
|
||||
session[:prj_old_nopublic] = params["nopublic"]
|
||||
session[:prj_old_tmpbld] = params["tmpbld"]
|
||||
if params["projname"].nil? || params["description"].nil? || params["projname"].strip == "" || params["description"].strip == "" || params["conflist"].nil? || params["conflist"].strip == ""
|
||||
session[:rcpcreate_error] = "Имя проекта, описание и окружение сборки не должны быть пустыми"
|
||||
redirect "/prjcreate"
|
||||
else
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
result = prj.create_project(params["projname"], params["description"], params["conflist"], params["nopublic"])
|
||||
result = prj.create_project(params["projname"], params["description"], params["conflist"], params["nopublic"], params["tmpbld"])
|
||||
if result != 0
|
||||
session[:prjcreate_error] = prj.error
|
||||
redirect "/prjcreate"
|
||||
@@ -355,6 +369,7 @@ post "/prjcreate" do
|
||||
session[:prj_old_description] = nil
|
||||
session[:prj_old_list] = nil
|
||||
session[:prj_old_nopublic] = nil
|
||||
session[:prj_old_tmpbld] = nil
|
||||
redirect "/projs"
|
||||
end
|
||||
end
|
||||
@@ -384,6 +399,7 @@ get "/prjedit/:id" do
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@proj_public = prj_info[:public]
|
||||
@proj_tmpbuild = prj_info[:tmpstpbuild]
|
||||
repo_lst = repo.getrepos
|
||||
proj_repo_list = prj.get_project_gits(prj_info[:id], repo)
|
||||
@repo_list = repo_lst.reject do |item|
|
||||
@@ -403,6 +419,177 @@ get "/prjedit/:id" do
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjclean/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@proj_public = prj_info[:public]
|
||||
@proj_tmpbuild = prj_info[:tmpstpbuild]
|
||||
|
||||
erb :prjclean1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjclean/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["delete"] == "delete" && !params["isclean"].nil?
|
||||
redirect "/prjclean_step2/#{params["id"]}"
|
||||
end
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjclean_step2/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_id = prj_info[:id]
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_repo(params["id"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
erb :prjclean2
|
||||
else
|
||||
print_error_page(503, "Файл не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjclean_step2/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["delete"] == "delete" && params["inputvernum"].to_i > 0
|
||||
numb = params["inputvernum"]
|
||||
redirect "/prjclean_step3/#{params["id"]}?numb=#{numb}"
|
||||
end
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjclean_step3/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["numb"].to_i > 0
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_repo(params["id"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
rpm_list_stay = []
|
||||
if File.exist?(f_path)
|
||||
rpm_list = get_rpms_list_full(f_path)
|
||||
repo = RepoManager.new(f_path)
|
||||
rpm_result_list = {}
|
||||
numb = params["numb"].to_i
|
||||
rpm_list.each do |item|
|
||||
f_name = File.basename item
|
||||
rpm_info = repo.get_rpm_info(item)
|
||||
if rpm_info[:error].nil?
|
||||
p_name = "#{rpm_info[:pkginfo].name}"
|
||||
if f_name =~ /\.src\.rpm$/
|
||||
p_name = "#{rpm_info[:pkginfo].name}_src"
|
||||
end
|
||||
if rpm_result_list[p_name].nil?
|
||||
rpm_result_list[p_name] = [[rpm_info[:pkginfo], item, rpm_info[:pkginfo].version]]
|
||||
else
|
||||
rpm_result_list[p_name] << [rpm_info[:pkginfo], item, rpm_info[:pkginfo].version]
|
||||
rpm_result_list[p_name].sort! do |a, b|
|
||||
if a[2] < b[2]
|
||||
1
|
||||
elsif a[2] > b[2]
|
||||
-1
|
||||
else
|
||||
a[2] <=> b[2]
|
||||
end
|
||||
end
|
||||
if rpm_result_list[p_name].length > numb
|
||||
rpm_result_list[p_name].pop
|
||||
end
|
||||
end
|
||||
else
|
||||
rpm_list_stay << item
|
||||
end
|
||||
|
||||
end
|
||||
rpm_result_list.each_pair do |k, v|
|
||||
v.each do |item|
|
||||
rpm_list_stay << item[1]
|
||||
end
|
||||
end
|
||||
|
||||
delete_rpm_list = rpm_list - rpm_list_stay
|
||||
@del_list = delete_rpm_list
|
||||
erb :prjclean3
|
||||
else
|
||||
print_error_page(503, "Файл не существует")
|
||||
end
|
||||
else
|
||||
print_error_page(503, "Число оставшихся пакетов должно быть более 0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjclean_step3/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["delete"] == "delete" && !params["prjrpmlst"].nil? && params["prjrpmlst"].length > 0
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
params["prjrpmlst"].each do |item|
|
||||
File.unlink(item)
|
||||
end
|
||||
prj.recreate_repo(@proj_id)
|
||||
@rpm_list = params["prjrpmlst"]
|
||||
erb :prjclean3_post
|
||||
else
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjagit/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
@@ -597,7 +784,6 @@ post "/prjcfg/:id" do
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
pp params
|
||||
unless params["cancel"].nil?
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
else
|
||||
@@ -765,7 +951,7 @@ get "/gitbld/:id/:git_id" do
|
||||
@git_name = git_info[:reponame]
|
||||
@proj_id = params["id"]
|
||||
|
||||
@build_id = prj.build_projects_git(prj_info[:id], git_info[:id], cfg.get_counter_path)
|
||||
@build_id = prj.build_projects_git(prj_info[:id], git_info[:id], cfg.get_counter_path, cfg.get_build_lock_path)
|
||||
|
||||
if @build_id == 0
|
||||
print_error_page(503, "Ошибка создания или получения информации о сборке, возможно проблемы с файлом блокировки")
|
||||
@@ -948,12 +1134,25 @@ get "/buildinfof/:build_id" do
|
||||
end
|
||||
end
|
||||
|
||||
get "/buildterm/:build_id" do
|
||||
build_info = db.get_build_info(params["build_id"])
|
||||
if build_info.nil?
|
||||
print_error_page(503, "Сборки не сущестует")
|
||||
else
|
||||
if build_info[:result] == 3
|
||||
db.update_build_task_status(params["build_id"], 4)
|
||||
end
|
||||
redirect back
|
||||
end
|
||||
end
|
||||
|
||||
get "/buildinfofraw" do
|
||||
if params["file"].nil? || !File.exist?(params["file"])
|
||||
print_error_page(503, "Файл не найден")
|
||||
else
|
||||
if !File.binary?(params["file"]) && params["file"].start_with?(File.join(Dir.pwd(), cfg.get_projects_path))
|
||||
send_file params["file"]
|
||||
f_name = File.basename(params["file"])
|
||||
send_file(params["file"], :filename => f_name, :type => "application/octet-stream", :disposition => 'attachment')
|
||||
else
|
||||
print_error_page(503, "Файл не может быть скачан")
|
||||
end
|
||||
@@ -1032,10 +1231,15 @@ get "/prjrpm/:id" do
|
||||
@file_content << "Версия пакета: #{rpm_info.version}"
|
||||
@file_content << ""
|
||||
@file_content << "Changelog:"
|
||||
rpm_info.changelog.first(10).each do |entry|
|
||||
@file_content << "#{entry.time} #{entry.name}"
|
||||
@file_content << "#{entry.text}"
|
||||
@file_content << "---------------"
|
||||
begin
|
||||
rpm_info.changelog.first(10).each do |entry|
|
||||
@file_content << "#{entry.time} #{entry.name}"
|
||||
@file_content << "#{entry.text}"
|
||||
@file_content << "---------------"
|
||||
end
|
||||
rescue
|
||||
# Если есть ошибка с undefined local variable or method, пропускаем changelog
|
||||
@file_content << "Changelog недоступен"
|
||||
end
|
||||
@file_content << "---------------"
|
||||
@file_content << "Файлы:"
|
||||
@@ -1246,18 +1450,379 @@ get "/prjsign/:id" do
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
gpgKeys = RepoManagerKeys.new(cgf.get_keys_path)
|
||||
gpgKeys = RepoManagerKeys.new(cfg.get_keys_path)
|
||||
if gpgKeys.check_key_exists
|
||||
@page_name = "Подписать проект #{prj_info[:projname]} "
|
||||
@pass_exists = gpgKeys.check_password_exists
|
||||
@page_name = "Подписать проект #{prj_info[:projname]}"
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_id = params["id"]
|
||||
erb :signconfirm
|
||||
else
|
||||
@page_name = "Ошибка подписания проекта #{prj_info[:projname]}"
|
||||
erb :gpgerror
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjsign/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["sign"] == "sign"
|
||||
err = prj.sign_project(params["id"], cfg.get_keys_path, params["password"], "/prjsignview/#{prj_info[:id]}", cfg.get_repoview_path)
|
||||
unless err.nil?
|
||||
session[:prj_modal_info] = "Ошибка добавления подписи пакетов проекта"
|
||||
session[:prj_modal_text] = err
|
||||
end
|
||||
end
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjremoteaddr/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
@page_name = "Подписать проект #{prj_info[:projname]}"
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_id = params["id"]
|
||||
@addres = prj_info[:remote_address]
|
||||
erb :projremote
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjremoteaddr/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["addr"] == "addr"
|
||||
err = prj.set_address(params["id"], params["address"])
|
||||
unless err.nil?
|
||||
session[:prj_modal_info] = "Ошибка установки адреса проекта"
|
||||
session[:prj_modal_text] = err
|
||||
end
|
||||
end
|
||||
if params["cancel"].nil? && params["reset"] == "reset"
|
||||
err = prj.set_address(params["id"], "")
|
||||
unless err.nil?
|
||||
session[:prj_modal_info] = "Ошибка установки адреса проекта"
|
||||
session[:prj_modal_text] = err
|
||||
end
|
||||
end
|
||||
redirect "/prjedit/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjsignview/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
gpgKeys = RepoManagerKeys.new(cfg.get_keys_path)
|
||||
if gpgKeys.check_key_exists
|
||||
redirect "/prjsignview/#{params["id"]}/"
|
||||
else
|
||||
@page_name = "Ошибка подписания проекта #{prj_info[:projname]}"
|
||||
erb :gpgerror
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjsignview/:id/*" do
|
||||
data_path = params[:splat]
|
||||
if data_path.nil? || data_path.first.strip == ""
|
||||
data_path = "index.html"
|
||||
end
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
gpgKeys = RepoManagerKeys.new(cfg.get_keys_path)
|
||||
if gpgKeys.check_key_exists
|
||||
sign_path = prj.get_sign_path(params["id"])
|
||||
f_path = File.join(sign_path, data_path)
|
||||
if File.exist? (f_path)
|
||||
send_file f_path
|
||||
else
|
||||
status 404
|
||||
end
|
||||
else
|
||||
@page_name = "Ошибка подписания проекта #{prj_info[:projname]}"
|
||||
erb :gpgerror
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjshot/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_repo(params["id"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
@rpms_list = []
|
||||
if File.exist?(f_path)
|
||||
@snap_list = prj.get_snap_list(prj_info[:id])
|
||||
unless params["snap"].nil?
|
||||
snap_shot = prj.get_project_snap(@proj_id, params["snap"])
|
||||
if File.exist?(snap_shot)
|
||||
@rpms_list = get_rpms_list(snap_shot)
|
||||
end
|
||||
end
|
||||
@rpms_list = [ "Снимок не выбран" ] if @rpms_list.length == 0
|
||||
erb :prjshot1
|
||||
else
|
||||
print_error_page(503, "Репозиторий не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjsnap_add/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_repo(params["id"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
erb :prjshot_add
|
||||
else
|
||||
print_error_page(503, "Репозиторий не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjsnap_add/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
if params["cancel"].nil? && params["create"] == "create" && !params["yes"].nil?
|
||||
prj.create_snapshot(prj_info[:id])
|
||||
end
|
||||
redirect "/prjshot/#{params["id"]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjsnap_delete/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@snap_name = params["snap"]
|
||||
if @snap_name.nil?
|
||||
print_error_page(503, "Не указано имя снимка")
|
||||
else
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_snap(params["id"], params["snap"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
erb :prjshot_delete
|
||||
else
|
||||
print_error_page(503, "Снимок не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjsnap_delete/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@snap_name = params["snap"]
|
||||
if @snap_name.nil?
|
||||
print_error_page(503, "Не указано имя снимка")
|
||||
else
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_snap(params["id"], params["snap"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
if params["cancel"].nil? && params["delete"] == "delete" && !params["yes"].nil?
|
||||
prj.delete_snapshot(prj_info[:id], @snap_name)
|
||||
end
|
||||
redirect "/prjshot/#{params["id"]}"
|
||||
else
|
||||
print_error_page(503, "Снимок не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
get "/prjsnap_restore/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@snap_name = params["snap"]
|
||||
if @snap_name.nil?
|
||||
print_error_page(503, "Не указано имя снимка")
|
||||
else
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_snap(params["id"], params["snap"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
erb :prjsnap_restore
|
||||
else
|
||||
print_error_page(503, "Снимок не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
post "/prjsnap_restore/:id" do
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
@page_name = prj_info[:projname]
|
||||
@proj_name = prj_info[:projname]
|
||||
@proj_descr = prj_info[:descr]
|
||||
@proj_id = prj_info[:id]
|
||||
@snap_name = params["snap"]
|
||||
if @snap_name.nil?
|
||||
print_error_page(503, "Не указано имя снимка")
|
||||
else
|
||||
filepath = ""
|
||||
proj_path = prj.get_project_snap(params["id"], params["snap"])
|
||||
f_path = File.join(proj_path, filepath)
|
||||
if File.exist?(f_path)
|
||||
if params["cancel"].nil? && params["restore"] == "restore" && !params["yes"].nil?
|
||||
prj.restore_snapshot(prj_info[:id], @snap_name)
|
||||
end
|
||||
redirect "/prjshot/#{params["id"]}"
|
||||
else
|
||||
print_error_page(503, "Снимок не существует")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
prj = ProjectsActions.new(cfg.get_projects_path, db)
|
||||
if prj.path.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
prj_info = prj.get_project(params["id"])
|
||||
if prj_info.nil?
|
||||
print_error_page(503, "Путь к проектам не существует")
|
||||
else
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
get "/sanitize" do
|
||||
#Подчистим гит проекты, которые есть в базе, но нет в файловой системе
|
||||
all_gits = db.get_gits
|
||||
deleted_items = 0
|
||||
unless all_gits.nil?
|
||||
repo = GitRepo.new(cfg.get_repo, db)
|
||||
repos = repo.getrepos
|
||||
list_for_delete = []
|
||||
all_gits.each do |item|
|
||||
find_item = false
|
||||
repos.each do |rep|
|
||||
if rep[:reponame] == item[:reponame]
|
||||
find_item = true
|
||||
break
|
||||
end
|
||||
end
|
||||
unless find_item
|
||||
list_for_delete << item[:id]
|
||||
end
|
||||
end
|
||||
deleted_items = deleted_items + list_for_delete.length
|
||||
list_for_delete.each do |item|
|
||||
db.delete_git_by_id(item)
|
||||
end
|
||||
end
|
||||
@deleted_items = deleted_items
|
||||
@page_name = "Очистка устаревших или потерянных записей базы данных"
|
||||
erb :sanitize
|
||||
end
|
||||
|
||||
get "/buildsclean" do
|
||||
@page_name = "Очистка окружений сборок"
|
||||
mock_cache_path = "/var/cache/mock/"
|
||||
@list_cleaned = get_dirs_in_mock_cache(mock_cache_path)
|
||||
MockManager.clean_mock
|
||||
erb :buildsclean
|
||||
end
|
||||
|
||||
not_found do
|
||||
status 404
|
||||
@page_name = "Кто-то потерялся"
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
require "inifile"
|
||||
|
||||
# Класс для работы с конфигурацией INI-файла
|
||||
class IniConfig
|
||||
attr :path
|
||||
|
||||
# Конструктор класса, принимает путь к INI-файлу и парсит его содержимое
|
||||
def initialize(in_path = "config.ini")
|
||||
@path = in_path
|
||||
@config = {}
|
||||
parse_ini
|
||||
end
|
||||
|
||||
# Метод для парсинга INI-файла и загрузки его содержимого в хэш
|
||||
def parse_ini()
|
||||
return if path.nil?
|
||||
return unless File.exist? path
|
||||
@config = IniFile.load(path)
|
||||
end
|
||||
|
||||
# Метод для получения порта сервера из конфигурации, если он задан, иначе возвращает значение по умолчанию (8080)
|
||||
def get_port()
|
||||
unless @config["server"]["port"].nil?
|
||||
@config["server"]["port"].to_i
|
||||
@@ -23,6 +27,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения репозитория из конфигурации, если он задан, иначе возвращает значение по умолчанию ("repo")
|
||||
def get_repo()
|
||||
unless @config["repo"]["repo"].nil?
|
||||
@config["repo"]["repo"].to_s
|
||||
@@ -31,6 +36,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения базы данных из конфигурации, если она задана, иначе возвращает значение по умолчанию ("db")
|
||||
def get_db()
|
||||
unless @config["server"]["db"].nil?
|
||||
@config["server"]["db"].to_s
|
||||
@@ -39,6 +45,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения скрытых конфигураций из конфигурации, если они заданы, иначе возвращает пустой массив
|
||||
def get_configs_hide()
|
||||
unless @config["configs"]["hide"].nil?
|
||||
@config["configs"]["hide"].split(",").map { |item| item.strip }
|
||||
@@ -47,6 +54,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения выбранных конфигураций из конфигурации, если они заданы, иначе возвращает пустой массив
|
||||
def get_configs_selected()
|
||||
unless @config["configs"]["selected"].nil?
|
||||
@config["configs"]["selected"].split(",").map { |item| item.strip }
|
||||
@@ -55,6 +63,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения пути к проектам из конфигурации, если он задан, иначе возвращает значение по умолчанию ("projects")
|
||||
def get_projects_path()
|
||||
unless @config["projects"]["path"].nil?
|
||||
@config["projects"]["path"].to_s
|
||||
@@ -63,6 +72,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения пути к счетчику из конфигурации, если он задан, иначе возвращает значение по умолчанию ("locks/counter")
|
||||
def get_counter_path()
|
||||
unless @config["counter"]["path"].nil?
|
||||
@config["counter"]["path"].to_s
|
||||
@@ -71,6 +81,16 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения пути к блокировке сборки из конфигурации, если он задан, иначе возвращает значение по умолчанию ("locks/build")
|
||||
def get_build_lock_path()
|
||||
unless @config["build"]["path"].nil?
|
||||
@config["build"]["path"].to_s
|
||||
else
|
||||
"locks/build"
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения количества элементов на странице из конфигурации, если оно задано и больше нуля, иначе возвращает значение по умолчанию (30)
|
||||
def get_items_per_page()
|
||||
unless @config["pages"]["items_per_page"].nil?
|
||||
res = @config["pages"]["items_per_page"].to_i
|
||||
@@ -83,6 +103,7 @@ class IniConfig
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения пути к ключам из конфигурации, если он задан, иначе возвращает значение по умолчанию ("keys")
|
||||
def get_keys_path()
|
||||
unless @config["sign"]["path"].nil?
|
||||
@config["sign"]["path"].to_s
|
||||
@@ -90,4 +111,13 @@ class IniConfig
|
||||
"keys"
|
||||
end
|
||||
end
|
||||
|
||||
# Метод для получения пути к реповьюверу из конфигурации, если он задан, иначе возвращает значение по умолчанию ("repoview")
|
||||
def get_repoview_path()
|
||||
unless @config["repoview"]["path"].nil?
|
||||
@config["repoview"]["path"].to_s
|
||||
else
|
||||
"repoview"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,27 +1,49 @@
|
||||
# Подключаем файл utilities.rb для использования его методов
|
||||
require_relative "utilities"
|
||||
|
||||
# Класс ConfigsList для работы со списком конфигураций
|
||||
class ConfigsList
|
||||
# Определение свойств класса
|
||||
attr :error, :cfg
|
||||
|
||||
# Конструктор класса с параметром cfg
|
||||
def initialize(cfg)
|
||||
@cfg = cfg
|
||||
end
|
||||
|
||||
# Метод get_configs для получения списка конфигураций
|
||||
def get_configs
|
||||
# Получаем список скрытых конфигураций из cfg
|
||||
hide_list = @cfg.get_configs_hide
|
||||
# Получаем список выбранных конфигураций из cfg
|
||||
select_list = @cfg.get_configs_selected
|
||||
|
||||
# Создаем список глобальных конфигураций в директории /etc/mock/*.cfg
|
||||
list_global = Dir["/etc/mock/*.cfg"].map { |item| [File.dirname(item), File.basename(item, ".cfg"), item] }.reject { |item| check_partname_in_array(item[1], hide_list) }
|
||||
|
||||
# Проверяем наличие глобальных конфигураций и создаем пустой список, если их нет
|
||||
if list_global.nil?
|
||||
list_global = []
|
||||
end
|
||||
|
||||
# Создаем список локальных конфигураций в директории ~/.config/mock/*.cfg
|
||||
list_local = Dir["~/.config/mock/*.cfg"].map { |item| [File.dirname(item), File.basename(item, ".cfg"), item] }
|
||||
|
||||
# Проверяем наличие локальных конфигураций и создаем пустой список, если их нет
|
||||
if list_local.nil?
|
||||
list_local = []
|
||||
end
|
||||
|
||||
# Создаем список выбранных конфигураций из глобального и локального списка
|
||||
list_selected = (list_global + list_local).select { |item| check_partname_in_array(item[1], select_list) }
|
||||
|
||||
# Проверяем наличие выбранных конфигураций и создаем пустой список, если их нет
|
||||
if list_selected.nil?
|
||||
list_selected = []
|
||||
end
|
||||
|
||||
# Возвращаем хеш с глобальными, локальными и выбранными конфигурациями
|
||||
{ :global => list_global, :local => list_local, :selected => list_selected }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -72,6 +72,7 @@ class DBase
|
||||
id = rep_id[:id]
|
||||
RepocRecips.where(repo_id: id).delete
|
||||
ReposProjects.where(repo_id: id).delete
|
||||
Repos.where(reponame: repo_name).delete
|
||||
end
|
||||
end
|
||||
|
||||
@@ -145,7 +146,7 @@ class DBase
|
||||
Projects[id]
|
||||
end
|
||||
|
||||
def proj_create(proj_name, proj_descr, nopublic)
|
||||
def proj_create(proj_name, proj_descr, nopublic, tmpbld)
|
||||
@error = nil
|
||||
data = Projects.where(projname: proj_name).first
|
||||
if data.nil?
|
||||
@@ -153,7 +154,11 @@ class DBase
|
||||
unless nopublic.nil?
|
||||
public_proj = 0
|
||||
end
|
||||
id = Projects.insert(projname: proj_name, descr: proj_descr, public: public_proj)
|
||||
tmpbld_proj = 1
|
||||
if tmpbld.nil?
|
||||
tmpbld_proj = 0
|
||||
end
|
||||
id = Projects.insert(projname: proj_name, descr: proj_descr, public: public_proj, tmpstpbuild: tmpbld_proj)
|
||||
@last_id = id
|
||||
else
|
||||
@error = "Данный проект уже существует"
|
||||
@@ -235,7 +240,7 @@ class DBase
|
||||
|
||||
#result = 0 (in progress), 1 (stopped - error), 2 (stopped - success)
|
||||
def create_build_task(prj_id, git_id, proj_path)
|
||||
id = BuildTask.insert(repo_id: git_id.to_i, proj_id: prj_id.to_i, signpath: "", logpath: "", errlogpath: "", result: 0)
|
||||
id = BuildTask.insert(repo_id: git_id.to_i, proj_id: prj_id.to_i, logpath: "", errlogpath: "", result: 0)
|
||||
@last_id = id
|
||||
BuildTask.where(id: id).update(logpath: File.join(proj_path, "#{id}"), errlogpath: File.join(proj_path, "#{id}", "process.log"))
|
||||
end
|
||||
@@ -244,6 +249,10 @@ class DBase
|
||||
BuildTask.where(id: build_id.to_i).update(result: status.to_i)
|
||||
end
|
||||
|
||||
def get_build_task_status(build_id)
|
||||
BuildTask.where(id: build_id.to_i).first
|
||||
end
|
||||
|
||||
def update_build_task_error_log(build_id, path)
|
||||
BuildTask.where(id: build_id.to_i).update(errlogpath: path)
|
||||
end
|
||||
@@ -311,7 +320,7 @@ class DBase
|
||||
end
|
||||
|
||||
def get_builds()
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc"].all
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, case when buildstart is null then 0 when buildstop is null then 0 else Cast((JulianDay(buildstop) - JulianDay(buildstart))*24*60*60 As Integer) end as timeproc, t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc"].all
|
||||
end
|
||||
|
||||
def get_build_info(build_id)
|
||||
@@ -332,11 +341,11 @@ class DBase
|
||||
end
|
||||
|
||||
def get_builds_for_project(prj_id)
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id where t1.proj_id = ? group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc", prj_id.to_i].all
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, case when buildstart is null then 0 when buildstop is null then 0 else Cast((JulianDay(buildstop) - JulianDay(buildstart))*24*60*60 As Integer) end as timeproc,t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id where t1.proj_id = ? group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc", prj_id.to_i].all
|
||||
end
|
||||
|
||||
def get_builds_for_project_git(prj_id, git_id)
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id where t1.proj_id = ? and t1.repo_id = ? group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc", prj_id.to_i, git_id.to_i].all
|
||||
$DDB["select t1.id as buildid, t1.create_at as createat, t1.result as state, case when buildstart is null then 0 when buildstop is null then 0 else Cast((JulianDay(buildstop) - JulianDay(buildstart))*24*60*60 As Integer) end as timeproc ,t2.reponame as reponame, t2.id as gitid, t3.id as projid, t3.projname as prjname, count(*) as pkgcnt from buildtask as t1 join repos as t2 on t1.repo_id = t2.id join projects as t3 on t1.proj_id = t3.id left join build_rpm as t4 on t4.build_id = t1.id where t1.proj_id = ? and t1.repo_id = ? group by buildid, createat, state, reponame, projid, prjname, gitid order by t1.id desc", prj_id.to_i, git_id.to_i].all
|
||||
end
|
||||
|
||||
def delete_git_from_project(prj_id, git_id)
|
||||
@@ -345,18 +354,62 @@ class DBase
|
||||
end
|
||||
|
||||
def delete_project(prj_id)
|
||||
result = ProjectsProjects.where(proj_id_repository: prj_id.to_i)
|
||||
count = 0
|
||||
result.each do |item|
|
||||
count = count + 1
|
||||
end
|
||||
return 1 if count > 0
|
||||
ReposProjects.where(proj_id: prj_id.to_i).delete
|
||||
ProjectsReposSpec.where(proj_id: prj_id.to_i).delete
|
||||
builds = BuildTask.where(proj_id: prj_id.to_i)
|
||||
builds.each do |item|
|
||||
rpms = BuildRpms.where(build_id: item[:id])
|
||||
Rpms.where(id: rpms[:rpm_id]).delete
|
||||
rpms.each do |rpm|
|
||||
rpm_id_t = rpm[:rpm_id]
|
||||
BuildRpms.where(build_id: item[:id], rpm_id: rpm_id_t).delete
|
||||
Rpms.where(id: rpm_id_t).delete
|
||||
end
|
||||
end
|
||||
BuildTask.where(proj_id: prj_id.to_i).delete
|
||||
ProjectsProjects.where(proj_id: prj_id.to_i).delete
|
||||
Projects.where(id: prj_id.to_i).delete
|
||||
0
|
||||
end
|
||||
|
||||
def projects_with_current_as_link(prj_id)
|
||||
ProjectsProjects.where(proj_id_repository: prj_id.to_i).all
|
||||
end
|
||||
|
||||
def get_rpm_info_by_hash(hash)
|
||||
Rpms.where(filehash: hash).first
|
||||
end
|
||||
|
||||
def update_rpm_sign(rpm_id, sign_path)
|
||||
Rpms.where(id: rpm_id.to_i).update(sign: 1, signpath: sign_path)
|
||||
end
|
||||
|
||||
def set_project_address(prj_id, address)
|
||||
Projects.where(id: prj_id.to_i).update(remote_address: address)
|
||||
end
|
||||
|
||||
def get_gits()
|
||||
Repos.all
|
||||
end
|
||||
|
||||
def delete_git_by_id(id)
|
||||
Repos.where(id: id.to_i).delete
|
||||
end
|
||||
|
||||
def cancel_hang_builds()
|
||||
BuildTask.where(result: [0, 3]).update(result: 4)
|
||||
end
|
||||
|
||||
def update_build_task_begin_time(build_id)
|
||||
BuildTask.where(id: build_id.to_i).update(buildstart: DateTime.now)
|
||||
end
|
||||
|
||||
def update_build_task_end_time(build_id)
|
||||
BuildTask.where(id: build_id.to_i).update(buildstop: DateTime.now)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -72,6 +72,24 @@ class GitRepo
|
||||
repos_data
|
||||
end
|
||||
|
||||
def git_walker(repo, tree, dir_name, result)
|
||||
tree.each_tree do |entry|
|
||||
new_tree = repo.lookup(entry[:oid])
|
||||
result = git_walker(repo, new_tree, "#{dir_name}/#{entry[:name]}", result)
|
||||
end
|
||||
tree.each_blob { |entry| result << "#{dir_name}/#{entry[:name]}" }
|
||||
result
|
||||
end
|
||||
|
||||
|
||||
def get_git_tree(repo, ref, result)
|
||||
|
||||
tree = ref.target.tree
|
||||
|
||||
result = git_walker(repo, tree, "", result)
|
||||
|
||||
end
|
||||
|
||||
def repo_info(reponame, branch = nil)
|
||||
info = {}
|
||||
result = ""
|
||||
@@ -97,6 +115,7 @@ class GitRepo
|
||||
info[:commits] = []
|
||||
info[:branches] = []
|
||||
info[:tags] = []
|
||||
info[:files] = []
|
||||
else
|
||||
ref = repo.head
|
||||
unless branch.nil?
|
||||
@@ -104,6 +123,7 @@ class GitRepo
|
||||
ref = repo.references[ref_name]
|
||||
end
|
||||
commits = []
|
||||
files = []
|
||||
unless ref.nil?
|
||||
walker = Rugged::Walker.new(repo)
|
||||
walker.sorting(Rugged::SORT_DATE)
|
||||
@@ -111,10 +131,15 @@ class GitRepo
|
||||
commits = walker.map do |commit|
|
||||
{ :message => commit.message, :author => commit.author, :time => commit.time, :sha => commit.oid }
|
||||
end.first(10)
|
||||
unless ref.target.nil? && ref.target.tree.nil?
|
||||
files = get_git_tree(repo, ref, files)
|
||||
end
|
||||
end
|
||||
|
||||
info[:commits] = commits
|
||||
info[:branches] = repo.branches.each_name(:local).sort
|
||||
info[:tags] = repo.tags.map { |tag| tag.name }
|
||||
info[:files] = files
|
||||
end
|
||||
else
|
||||
@error = result
|
||||
|
||||
114
classes/mock.rb
@@ -4,6 +4,7 @@ require "fileutils"
|
||||
require "logger"
|
||||
require_relative "repomanage"
|
||||
require "digest"
|
||||
require "date"
|
||||
|
||||
BUILD_STRUCTURE = {
|
||||
:SRC => "src",
|
||||
@@ -13,9 +14,9 @@ BUILD_STRUCTURE = {
|
||||
}
|
||||
|
||||
class MockManager
|
||||
attr :path, :config, :error, :last_status, :last_pid, :prep_dir, :db, :resultpath, :process_log, :repo_path, :git_path, :build_id, :log, :recips, :spec, :repo_lock, :git_id
|
||||
attr :path, :config, :error, :last_status, :last_pid, :prep_dir, :db, :resultpath, :process_log, :repo_path, :git_path, :build_id, :log, :recips, :spec, :repo_lock, :git_id, :tmp_bld
|
||||
|
||||
def initialize(path, config, cfg_counter_path, db, result_path, repo_path, git_path, build_id, recips, spec_file, repo_lock, git_id)
|
||||
def initialize(path, config, cfg_counter_path, db, result_path, repo_path, git_path, build_id, recips, spec_file, repo_lock, git_id, tmp_bld)
|
||||
@error = nil
|
||||
unless File.exist? (path)
|
||||
Dir.mkdir(path)
|
||||
@@ -32,6 +33,7 @@ class MockManager
|
||||
@spec = spec_file
|
||||
@repo_lock = repo_lock
|
||||
@git_id = git_id
|
||||
@tmp_bld = tmp_bld
|
||||
|
||||
File.open(cfg_counter_path, "r+") do |f|
|
||||
f.flock(File::LOCK_EX)
|
||||
@@ -110,6 +112,39 @@ class MockManager
|
||||
@log.info("Подготовка SRCRPM")
|
||||
spec_file = File.join(@tmp_src, @spec)
|
||||
if File.exist?(spec_file)
|
||||
if @tmp_bld
|
||||
dt = DateTime.now
|
||||
bld_id = @build_id.to_i
|
||||
bld_str = "%010d" % bld_id
|
||||
dt_str = dt.strftime("%Y%m%d_#{bld_str}")
|
||||
cmd_args = %Q(/usr/bin/rpm -q --specfile #{spec_file} --queryformat "%{RELEASE}\n" 2>/dev/null)
|
||||
@log.debug(cmd_args)
|
||||
cmd = Runner.new(cmd_args, @log)
|
||||
cmd.run
|
||||
if cmd.exit_status == 0
|
||||
res = "#{cmd.stdout}.#{dt_str}"
|
||||
versions = "#{cmd.stdout}".split("\n")
|
||||
if versions.length()>0
|
||||
res = versions[0].strip
|
||||
res = "#{res}.#{dt_str}"
|
||||
else
|
||||
res = cmd.stdout.strip
|
||||
res = "#{res}.#{dt_str}"
|
||||
end
|
||||
line_array = []
|
||||
File.readlines(spec_file).each do |line|
|
||||
if line =~ /^[\t ]*[Rr]elease:/
|
||||
line = "Release: #{res}"
|
||||
end
|
||||
line_array << line
|
||||
end
|
||||
File.open(spec_file, "w") do |f|
|
||||
line_array.each do |line|
|
||||
f.puts(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Dir.chdir(@tmp_src) do
|
||||
cmd_args = %Q(/usr/bin/mock -r "#{@config}" --buildsrpm --spec "#{spec_file}" --sources "#{@tmp_src}" --resultdir "#{File.join(@prep_dir, BUILD_STRUCTURE[:RESULT_SRPM])}" --isolation=simple --disable-plugin=ccache)
|
||||
@log.debug(cmd_args)
|
||||
@@ -240,7 +275,7 @@ class MockManager
|
||||
end
|
||||
end
|
||||
|
||||
def build_task()
|
||||
def build_task(build_lock)
|
||||
@error = false
|
||||
@db.before_fork
|
||||
spock = Spork.spork(:logger => log) do
|
||||
@@ -248,33 +283,56 @@ class MockManager
|
||||
old_stdout = $stdout.dup
|
||||
$stdout = File.open(@process_log, "w")
|
||||
@log = Logger.new($stdout)
|
||||
if @spec == ""
|
||||
@error = true
|
||||
@log.error("Не могу найти spec файл")
|
||||
end
|
||||
begin
|
||||
prepare_structure if @error == false
|
||||
prepare_src if @error == false
|
||||
prepare_source if @error == false
|
||||
prepare_src_rpm if @error == false
|
||||
build_rpm if @error == false
|
||||
save_logs
|
||||
save_rpms if @error == false
|
||||
rescue => e
|
||||
@error = true
|
||||
puts e
|
||||
end
|
||||
$stdout = old_stdout
|
||||
@log.close
|
||||
save_prg_log
|
||||
clean_build
|
||||
if @error
|
||||
@db.update_build_task_status(@build_id, 1)
|
||||
else
|
||||
@db.update_build_task_status(@build_id, 2)
|
||||
|
||||
File.open(build_lock,"wb") do |global_lock|
|
||||
global_lock.flock(File::LOCK_EX)
|
||||
global_lock.rewind
|
||||
build_info = @db.get_build_task_status(build_id)
|
||||
unless build_info.nil?
|
||||
if build_info[:result].to_i == 4
|
||||
return
|
||||
end
|
||||
end
|
||||
@db.update_build_task_status(@build_id, 0)
|
||||
@db.update_build_task_begin_time(@build_id)
|
||||
|
||||
if @spec == ""
|
||||
@error = true
|
||||
@log.error("Не могу найти spec файл")
|
||||
end
|
||||
begin
|
||||
prepare_structure if @error == false
|
||||
prepare_src if @error == false
|
||||
prepare_source if @error == false
|
||||
prepare_src_rpm if @error == false
|
||||
build_rpm if @error == false
|
||||
save_logs
|
||||
save_rpms if @error == false
|
||||
rescue => e
|
||||
@error = true
|
||||
puts e
|
||||
end
|
||||
$stdout = old_stdout
|
||||
@log.close
|
||||
save_prg_log
|
||||
clean_build
|
||||
if @error
|
||||
@db.update_build_task_status(@build_id, 1)
|
||||
else
|
||||
@db.update_build_task_status(@build_id, 2)
|
||||
end
|
||||
@db.update_build_task_end_time(@build_id)
|
||||
global_lock.flock(File::LOCK_UN)
|
||||
|
||||
end
|
||||
end
|
||||
@db.after_fork
|
||||
spock
|
||||
|
||||
end
|
||||
|
||||
def self.clean_mock
|
||||
cmd_args = %Q(/usr/bin/mock --scrub-all-chroots)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run_clean
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,7 @@ require_relative "db"
|
||||
require_relative "repomanage"
|
||||
require_relative "mock"
|
||||
require_relative "utilities"
|
||||
require "digest"
|
||||
|
||||
PROJECTS_STRUCTURE = {
|
||||
:REPO => "repo",
|
||||
@@ -11,6 +12,7 @@ PROJECTS_STRUCTURE = {
|
||||
:SRCPRP => "srcprp",
|
||||
:SIGNED => "signed",
|
||||
:SRC => "src",
|
||||
:SNAP => "snapshot",
|
||||
}
|
||||
|
||||
class ProjectsActions
|
||||
@@ -70,6 +72,11 @@ class ProjectsActions
|
||||
File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
|
||||
end
|
||||
|
||||
def get_project_snap(id, snap)
|
||||
proj_path = get_project_path(id)
|
||||
File.join(proj_path, PROJECTS_STRUCTURE[:SNAP], snap)
|
||||
end
|
||||
|
||||
def get_project_path_git(id, gitname)
|
||||
proj_path = get_project_path(id)
|
||||
File.join(proj_path, PROJECTS_STRUCTURE[:SRC], gitname)
|
||||
@@ -98,11 +105,13 @@ class ProjectsActions
|
||||
internal_repo_path = File.join(internal_path, PROJECTS_STRUCTURE[:REPO])
|
||||
internal_proj_info = internal_repo.get_project(item[:proj_id_repository])
|
||||
proj_repo = <<~PRJ_CFG
|
||||
config_opts['dnf.conf'] += """
|
||||
[#{internal_proj_info[:projname]}-repository]
|
||||
name=Project repository #{internal_proj_info[:projname]}
|
||||
baseurl=file://#{internal_repo_path}
|
||||
enabled=1
|
||||
skip_if_unavailable=True
|
||||
"""
|
||||
PRJ_CFG
|
||||
linked_prj << proj_repo
|
||||
end
|
||||
@@ -144,7 +153,7 @@ class ProjectsActions
|
||||
generate_linked_repos(id, proj_path, proj_name, prj_incl_path)
|
||||
end
|
||||
|
||||
def create_project(name, description, configuration, nopublic)
|
||||
def create_project(name, description, configuration, nopublic, tmpbld)
|
||||
@error = nil
|
||||
ret_val = 0
|
||||
project_name = sanitize_rcptname(name)
|
||||
@@ -162,7 +171,7 @@ class ProjectsActions
|
||||
end
|
||||
if File.exist?(configuration)
|
||||
generate_config(nil, configuration, fname, project_name)
|
||||
@error = @db.proj_create(project_name, description, nopublic)
|
||||
@error = @db.proj_create(project_name, description, nopublic, tmpbld)
|
||||
if @error.nil?
|
||||
created = true
|
||||
end
|
||||
@@ -266,7 +275,7 @@ class ProjectsActions
|
||||
spec_file
|
||||
end
|
||||
|
||||
def build_projects_git(prj_id, git_id, counter_file)
|
||||
def build_projects_git(prj_id, git_id, counter_file, build_lock)
|
||||
bld_id = 0
|
||||
build_ok = true
|
||||
proj_path = get_project_path(prj_id)
|
||||
@@ -280,20 +289,16 @@ class ProjectsActions
|
||||
File.open(lockf_path, File::RDWR | File::CREAT) do |f|
|
||||
result = f.flock(File::LOCK_EX | File::LOCK_NB)
|
||||
if result == false
|
||||
#Файл заблокирован считать id и вывести сведения о сборке
|
||||
# Файл заблокирован считать id и вывести сведения о сборке
|
||||
build_ok = false
|
||||
build_id = f.gets
|
||||
unless build_id.nil?
|
||||
build_id = build_id.strip.to_i
|
||||
end
|
||||
if build_id > 0
|
||||
build_id = build_id.strip.to_i unless build_id.nil?
|
||||
if build_id.positive?
|
||||
build_info = @db.get_build_task_process_log(build_id)
|
||||
unless build_info.nil?
|
||||
bld_id = build_info[:id]
|
||||
end
|
||||
bld_id = build_info[:id] unless build_info.nil?
|
||||
end
|
||||
else
|
||||
#Сборка завершилась, но каталог не подчистился
|
||||
# Сборка завершилась, но каталог не подчистился
|
||||
FileUtils.rm_rf(prepare_path)
|
||||
f.flock(File::LOCK_UN)
|
||||
build_ok = true
|
||||
@@ -303,23 +308,27 @@ class ProjectsActions
|
||||
|
||||
#Верная ситуация
|
||||
if build_ok
|
||||
build_path = File.join(proj_path, PROJECTS_STRUCTURE[:LOGS], git_name[:reponame])
|
||||
Dir.mkdir(prepare_path)
|
||||
lockf_path = File.join(prepare_path, "lock")
|
||||
File.open(lockf_path, File::RDWR | File::CREAT) do |f|
|
||||
f.flock(File::LOCK_EX)
|
||||
f.rewind
|
||||
#Начинаем сборку
|
||||
build_path = File.join(proj_path, PROJECTS_STRUCTURE[:LOGS], git_name[:reponame])
|
||||
#Начинаем сборку
|
||||
@db.create_build_task(prj_id, git_id, build_path)
|
||||
build_id = @db.last_id
|
||||
repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
|
||||
git_source = File.join(proj_path, PROJECTS_STRUCTURE[:SRC], git_name[:reponame])
|
||||
@db.create_build_task(prj_id, git_id, build_path)
|
||||
build_id = @db.last_id
|
||||
f.puts(build_id)
|
||||
f.flush
|
||||
mock = MockManager.new(prepare_path, get_project_config(prj_id), counter_file, @db, build_path, repo_path, git_source, build_id, prep_script, spec_file, repo_lock, git_id)
|
||||
proj_info = get_project(prj_id)
|
||||
tmp_bld = false
|
||||
tmp_bld = true if proj_info[:tmpstpbuild].to_i != 0
|
||||
mock = MockManager.new(prepare_path, get_project_config(prj_id), counter_file, @db, build_path, repo_path, git_source, build_id, prep_script, spec_file, repo_lock, git_id, tmp_bld)
|
||||
bld_id = build_id
|
||||
@db.update_build_task_error_log(build_id, mock.get_build_process_log)
|
||||
mock.build_task
|
||||
@db.update_build_task_status(build_id, 3)
|
||||
mock.build_task(build_lock)
|
||||
end
|
||||
end
|
||||
bld_id
|
||||
@@ -364,11 +373,169 @@ class ProjectsActions
|
||||
if linked.nil? || linked.length == 0
|
||||
proj_path = get_project_path(prj_id)
|
||||
FileUtils.rm_rf(proj_path, secure: true)
|
||||
@db.delete_project(prj_id)
|
||||
if @db.delete_project(prj_id) != 0
|
||||
@error = "На текущий проект ссылаются другие проекты. Удаление запрещено"
|
||||
end
|
||||
else
|
||||
@error = "На текущий проект ссылаются другие проекты. Удаление запрещено"
|
||||
end
|
||||
end
|
||||
@error
|
||||
end
|
||||
|
||||
def sign_project(prj_id, key_path, password, url, tpl_dir)
|
||||
@error = nil
|
||||
proj_path = get_project_path(prj_id)
|
||||
sign_repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:SIGNED])
|
||||
repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
|
||||
repo_sign = RepoManager.new(sign_repo_path)
|
||||
repo_key = RepoManagerKeys.new(key_path)
|
||||
if password.nil?
|
||||
password = repo_key.check_password_exists
|
||||
end
|
||||
if password.nil?
|
||||
@error = "Не указан пароль для подписи"
|
||||
else
|
||||
repo_lock = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], ".repolock")
|
||||
sign_lock = File.join(proj_path, PROJECTS_STRUCTURE[:CONFIGS], ".signlock")
|
||||
prj = @db.proj(prj_id)
|
||||
if repo_key.check_key_exists
|
||||
File.open(sign_lock, File::RDWR | File::CREAT) do |s|
|
||||
s.flock(File::LOCK_EX)
|
||||
File.open(repo_lock, File::RDWR | File::CREAT) do |f|
|
||||
f.flock(File::LOCK_EX)
|
||||
rpm_list = get_rpms_list(repo_path)
|
||||
if prj[:public] == 0
|
||||
rpm_list = rpm_list.reject do |item|
|
||||
block = false
|
||||
block = true if item =~ /\.src\.rpm$/ || item =~ /SRPMS/ || item =~ /Debug/ || item =~ /(debuginfo.+rpm$)|(debugsource.+rpm$)/
|
||||
block
|
||||
end
|
||||
end
|
||||
rpm_signed_list = get_rpms_list(sign_repo_path)
|
||||
if rpm_signed_list.length > rpm_list.length
|
||||
rpms_for_delete = get_rpms_list_full(sign_repo_path)
|
||||
rpms_for_delete.each do |item|
|
||||
File.unlink(item)
|
||||
end
|
||||
end
|
||||
rpm_list = rpm_list.select do |item|
|
||||
sign_repo_path_rpm = File.join(sign_repo_path, item)
|
||||
unless File.exist?(sign_repo_path_rpm)
|
||||
file_path_full = File.join(repo_path, item)
|
||||
unless File.exist?(File.dirname(sign_repo_path_rpm))
|
||||
FileUtils.mkdir_p(File.dirname(sign_repo_path_rpm))
|
||||
end
|
||||
FileUtils.cp_r(file_path_full, File.dirname(sign_repo_path_rpm), verbose: false, remove_destination: false)
|
||||
sha256 = Digest::SHA256.file(file_path_full)
|
||||
rpm_info = @db.get_rpm_info_by_hash(sha256.hexdigest)
|
||||
unless rpm_info.nil?
|
||||
@db.update_rpm_sign(rpm_info[:id], sign_repo_path_rpm)
|
||||
end
|
||||
repo_key.sign_package(sign_repo_path_rpm, password)
|
||||
end
|
||||
end
|
||||
repo_url = "http://localhost/"
|
||||
if prj[:remote_address].nil? || prj[:remote_address].strip == ""
|
||||
repo_url = url
|
||||
else
|
||||
repo_url = prj[:remote_address]
|
||||
end
|
||||
if repo_url[-1] != "/"
|
||||
repo_url = repo_url + "/"
|
||||
end
|
||||
repo_sign.repoview(repo_url, prj[:projname], tpl_dir)
|
||||
repo_sign.create_repo
|
||||
pub_key = repo_key.get_publick_key
|
||||
proj_repo_key = File.join(sign_repo_path, "#{prj[:projname]}-gpg-key")
|
||||
unless File.exist?(proj_repo_key)
|
||||
FileUtils.cp_r(pub_key, proj_repo_key, verbose: false, remove_destination: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
@error = "Ключ для подписи отсутствует"
|
||||
end
|
||||
end
|
||||
@error
|
||||
end
|
||||
|
||||
def set_address(prj_id, address)
|
||||
@error = nil
|
||||
if address.nil?
|
||||
address = ""
|
||||
else
|
||||
address = address.strip
|
||||
end
|
||||
@db.set_project_address(prj_id, address)
|
||||
@error
|
||||
end
|
||||
|
||||
def get_sign_path(id)
|
||||
path = get_project_path(id)
|
||||
File.join(path, PROJECTS_STRUCTURE[:SIGNED])
|
||||
end
|
||||
|
||||
def recreate_repo(id)
|
||||
proj_path = get_project_path(id)
|
||||
repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
|
||||
repoman = RepoManager.new(repo_path)
|
||||
repoman.create_repo
|
||||
end
|
||||
|
||||
def get_snap_list(id)
|
||||
proj_path = get_project_path(id)
|
||||
snap_path = File.join(proj_path, PROJECTS_STRUCTURE[:SNAP])
|
||||
|
||||
unless Dir.exist?(snap_path)
|
||||
FileUtils.mkdir_p(snap_path)
|
||||
end
|
||||
|
||||
snap_list = Dir.glob(File.join(snap_path, "*")).select { |path| File.directory?(path) }.map { |dir| File.basename(dir) }
|
||||
|
||||
return snap_list
|
||||
end
|
||||
|
||||
def create_snapshot(id)
|
||||
proj_path = get_project_path(id)
|
||||
snap_path = File.join(proj_path, PROJECTS_STRUCTURE[:SNAP])
|
||||
repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
|
||||
|
||||
unless Dir.exist?(snap_path)
|
||||
FileUtils.mkdir_p(snap_path)
|
||||
end
|
||||
|
||||
date_str = Time.now.strftime("%Y-%m-%d")
|
||||
snapshot_dir = "#{date_str}_0"
|
||||
|
||||
while Dir.exist?(File.join(snap_path, snapshot_dir))
|
||||
num = snapshot_dir.split("_").last.to_i
|
||||
num += 1
|
||||
snapshot_dir = "#{date_str}_#{num}"
|
||||
end
|
||||
|
||||
snapshot_full_path = File.join(snap_path, snapshot_dir)
|
||||
FileUtils.cp_r(repo_path, snapshot_full_path)
|
||||
end
|
||||
|
||||
def delete_snapshot(id, snap)
|
||||
proj_path = get_project_path(id)
|
||||
snap_path = get_project_snap(id, snap)
|
||||
snap_path_base = File.join(proj_path, PROJECTS_STRUCTURE[:SNAP])
|
||||
unless snap_path == snap_path_base || "#{snap_path}/" == snap_path_base || snap_path == "#{snap_path_base}/"
|
||||
if File.exist?(snap_path)
|
||||
FileUtils.rm_rf(snap_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def restore_snapshot(id, snap)
|
||||
repo_path = get_project_repo(id)
|
||||
snap_path = get_project_snap(id, snap)
|
||||
|
||||
if Dir.exist?(repo_path) && Dir.exist?(snap_path)
|
||||
Dir.glob(File.join(repo_path, '*')).each { |file| File.unlink(file) if File.file?(file) || FileUtils.rm_rf(file) }
|
||||
FileUtils.cp_r(Dir.glob(File.join(snap_path, '*')), repo_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,6 +3,9 @@ $LOAD_PATH.unshift File.expand_path(".", "locallibs/ruby-rpm-ffi/lib")
|
||||
require "rpm"
|
||||
|
||||
require_relative "runner"
|
||||
require "ptools"
|
||||
require "fileutils"
|
||||
require "erb"
|
||||
|
||||
class RPMReader
|
||||
def get_rpm_info(path_to_rpm)
|
||||
@@ -29,9 +32,29 @@ class RepoManagerKeys
|
||||
key_file = File.join(@path, "public", "mockgui-gpg-key")
|
||||
File.exist?(key_file)
|
||||
end
|
||||
end
|
||||
|
||||
#rpm --define "_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase 1234" --addsign bayrepo-neuro-farm-0.1-2.x86_64.rpm
|
||||
def check_password_exists()
|
||||
passwd = nil
|
||||
passwd_file = File.join(@path, "save")
|
||||
if File.exist?(passwd_file)
|
||||
unless File.binary?(passwd_file)
|
||||
passwd = File.readlines(passwd_file).first.strip
|
||||
end
|
||||
end
|
||||
passwd
|
||||
end
|
||||
|
||||
def get_publick_key()
|
||||
File.join(@path, "public", "mockgui-gpg-key")
|
||||
end
|
||||
|
||||
def sign_package(rpm_path, password)
|
||||
cmd_args = %Q(/usr/bin/rpm --define "_gpg_sign_cmd_extra_args --pinentry-mode loopback --passphrase #{password}" --addsign "#{rpm_path}" 2>/dev/null)
|
||||
cmd = Runner.new(cmd_args)
|
||||
cmd.run
|
||||
cmd.exit_status
|
||||
end
|
||||
end
|
||||
|
||||
class RepoManager
|
||||
attr :path, :error, :last_status, :last_pid
|
||||
@@ -60,4 +83,92 @@ class RepoManager
|
||||
def get_rpm_info(path_to_rpm)
|
||||
@reader.get_rpm_info(path_to_rpm)
|
||||
end
|
||||
|
||||
def repoview(url, repo_name, template_dir)
|
||||
rpm_list = get_rpms_list(@path)
|
||||
result = {}
|
||||
nresult = {}
|
||||
rpm_list.each do |item|
|
||||
full_rpm_path = File.join(@path, item)
|
||||
info = @reader.get_rpm_info(full_rpm_path)
|
||||
dirName = File.dirname(item)
|
||||
fileName = File.basename(item)
|
||||
if result[dirName].nil?
|
||||
result[dirName] = {}
|
||||
end
|
||||
nresult[dirName] = [] if nresult[dirName].nil?
|
||||
pkg_info = {}
|
||||
pkg_info[:fname] = fileName
|
||||
pkg_info[:aname] = item
|
||||
pkg_info[:stat] = File.stat(full_rpm_path).ctime
|
||||
pkg_info[:pname] = "noname"
|
||||
if info[:error].nil?
|
||||
pkg_info[:chlog] = []
|
||||
begin
|
||||
pkg_info[:chlog] << info[:pkginfo].changelog.first
|
||||
rescue
|
||||
end
|
||||
pkg_info[:pname] = info[:pkginfo].name
|
||||
else
|
||||
pkg_info[:chlog] = []
|
||||
end
|
||||
result[dirName][pkg_info[:pname]] = [] unless result[dirName].key?(pkg_info[:pname])
|
||||
result[dirName][pkg_info[:pname]] << pkg_info
|
||||
nresult[dirName] << pkg_info
|
||||
end
|
||||
repo_name = repo_name
|
||||
repo_url = url
|
||||
pkg_num = rpm_list.length
|
||||
repo_data = []
|
||||
data_keys = []
|
||||
lresult = {}
|
||||
result.each_pair do |key, value|
|
||||
pak_keys = []
|
||||
value.each_pair do |pkey, pvalue|
|
||||
result[key][pkey].sort_by! { |item| item[:fname] }
|
||||
pak_keys << pkey.to_s
|
||||
end
|
||||
pak_keys.sort!
|
||||
lresult[key] = [] unless lresult.key?(key.to_s)
|
||||
pak_keys.each do |item|
|
||||
lresult[key] << [value[item], item]
|
||||
end
|
||||
|
||||
data_keys << key.to_s
|
||||
end
|
||||
|
||||
data_keys.sort!
|
||||
data_keys.each do |item|
|
||||
repo_data << [lresult[item], item]
|
||||
end
|
||||
|
||||
last_update_src = []
|
||||
if nresult.key?("SRPMS")
|
||||
last_update_src = nresult["SRPMS"].map do |record|
|
||||
{ fname: record[:fname], stat: record[:stat] }
|
||||
end.sort_by do |item|
|
||||
[item[:stat], item[:fname].downcase.ord]
|
||||
end.reverse.map do |record|
|
||||
if record[:stat].nil?
|
||||
["нет даты", record[:fname]]
|
||||
else
|
||||
[record[:stat].strftime("%Y-%m-%d"), record[:fname]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tpl_file = File.join(template_dir, "template.erb")
|
||||
template = File.read(tpl_file)
|
||||
renderer = ERB.new(template)
|
||||
result_html = renderer.result(binding)
|
||||
|
||||
boots_trap_css = File.join(template_dir, "bootstrap.min.css")
|
||||
boots_trap_js = File.join(template_dir, "bootstrap.bundle.min.js")
|
||||
index_html = File.join(@path, "index.html")
|
||||
File.open(index_html, "w") do |f|
|
||||
f.write(result_html)
|
||||
end
|
||||
FileUtils.cp_r(boots_trap_css, @path, verbose: false, remove_destination: true)
|
||||
FileUtils.cp_r(boots_trap_js, @path, verbose: false, remove_destination: true)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,3 +50,15 @@ end
|
||||
def get_log_paths_success(directory)
|
||||
Dir.glob(File.join(directory, "**", "*")).reject { |f| File.directory?(f) }.select { |f| File.extname(f) == ".log" }.reject { |f| File.basename(f) == "process.log" }
|
||||
end
|
||||
|
||||
def get_rpms_list(directory)
|
||||
Dir.glob(File.join(directory, "**", "*.rpm")).reject { |f| File.directory?(f) || f =~ /repodata\// }.map { |f| f.delete_prefix(directory + "/") }
|
||||
end
|
||||
|
||||
def get_dirs_in_mock_cache(dir_path)
|
||||
Dir.glob(File.join(dir_path, "*")).select { |f| File.directory?(f) }.map{ |f| File.basename(f) }
|
||||
end
|
||||
|
||||
def get_rpms_list_full(directory)
|
||||
Dir.glob(File.join(directory, "**", "*.rpm")).reject { |f| File.directory?(f) || f =~ /repodata\// }
|
||||
end
|
||||
|
||||
12
config.ini
@@ -12,12 +12,18 @@ old = 3
|
||||
[counter]
|
||||
path = "locks/counter"
|
||||
|
||||
[build]
|
||||
path = "locks/build"
|
||||
|
||||
[configs]
|
||||
hide=open,amazon,anolis,circle,custom,euro,fedora,mageia,navy,alma,rocky
|
||||
selected=msvsphere
|
||||
hide=open,amazon,anolis,circle,custom,euro,fedora,mageia,navy,rocky
|
||||
selected=alma
|
||||
|
||||
[pages]
|
||||
items_per_page = 30
|
||||
|
||||
[sign]
|
||||
path = "keys"
|
||||
path = "keys"
|
||||
|
||||
[repoview]
|
||||
path = "repoview"
|
||||
@@ -15,6 +15,8 @@ Sequel.migration do
|
||||
String :projname
|
||||
String :descr, text: true
|
||||
Integer :public
|
||||
String :remote_address, text: true
|
||||
String :remote_command, text: true
|
||||
Datetime :create_at, default: Sequel.lit("CURRENT_TIMESTAMP")
|
||||
end
|
||||
|
||||
@@ -53,7 +55,6 @@ Sequel.migration do
|
||||
foreign_key :repo_id, :repos, :key => :id
|
||||
foreign_key :proj_id, :projects, :key => :id
|
||||
Datetime :create_at, default: Sequel.lit("CURRENT_TIMESTAMP")
|
||||
String :signpath, text: true
|
||||
String :logpath, text: true
|
||||
Integer :result
|
||||
String :errlogpath, text: true
|
||||
|
||||
9
db/migrations/202511040000000_aletrtables.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
require "sequel"
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:projects) do
|
||||
add_column :tmpstpbuild, Integer, default: 0
|
||||
end
|
||||
end
|
||||
end
|
||||
6
db/migrations/202511140000000_create_queue.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
require "sequel"
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
end
|
||||
end
|
||||
12
db/migrations/202511300000000_update.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
require "sequel"
|
||||
|
||||
Sequel.migration do
|
||||
change do
|
||||
alter_table(:buildtask) do
|
||||
add_column :buildstart, DateTime
|
||||
add_column :buildstop, DateTime
|
||||
set_column_allow_null :buildstart
|
||||
set_column_allow_null :buildstop
|
||||
end
|
||||
end
|
||||
end
|
||||
19
docs/mock-gui/docs/common.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Общий принцип работы с MockGUI
|
||||
|
||||
Система является однопользовательской.
|
||||
|
||||
Рекомендуется устанавливать ее на локальной машине в локальной сети.
|
||||
|
||||
## Текущие ограничения
|
||||
|
||||
* Одновременно может запускаться только одна сборка.
|
||||
* Сборка потребляет все доступные ядра процессора
|
||||
* На текущий момент система является alpha сборкой, поэтому подавление ошибок не включено, для детального отслеживания ошибок
|
||||
|
||||
## Особые указания
|
||||
|
||||
Внизу страницы веб интерфейса есть нижняя панель управления. Там имеется два сервисных пункта меню:
|
||||
|
||||
`Сбросить buildroots` - со временем, при большом количестве сборок, кэш mock-билдера может разрастаться, для освобождения кэшей рекомендуется периодически запускать сброс с помощью данного пункта меню.
|
||||
|
||||
`Подчистка` - проводится инвентаризация гит-проектов и если в базе данных нет записи, то она добавляется. Этот пункт дает возможность подложить гит-проекты и одним нажатием добавить их в веб-интерфейс.
|
||||
22
docs/mock-gui/docs/docs.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Документация
|
||||
|
||||
## Сборка локальной документации
|
||||
|
||||
```shell
|
||||
cd docs/mock-gui
|
||||
pip install mkdocs
|
||||
pip install markupsafe==2.0.1
|
||||
pip install mkdocs-bootstrap
|
||||
```
|
||||
|
||||
Локальный сервер:
|
||||
|
||||
```shell
|
||||
mkdocs serve
|
||||
```
|
||||
|
||||
Или статическая документация:
|
||||
|
||||
```shell
|
||||
mkdocs build
|
||||
```
|
||||
BIN
docs/mock-gui/docs/img/mockgui_build10.png
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
docs/mock-gui/docs/img/mockgui_builds1.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit1.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit2.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit3.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit4.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit5.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/mock-gui/docs/img/mockgui_creategit5_1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/mock-gui/docs/img/mockgui_deletegit1.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/mock-gui/docs/img/mockgui_env1.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
BIN
docs/mock-gui/docs/img/mockgui_env2.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/mock-gui/docs/img/mockgui_list1.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
docs/mock-gui/docs/img/mockgui_list2.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
docs/mock-gui/docs/img/mockgui_mainscreen.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docs/mock-gui/docs/img/mockgui_project1.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects10.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects14.png
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects15.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects17.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects2.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects20.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects20_1.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects20_2.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects20_3.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects25.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects26.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects27.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects3.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects4.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects5.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects6.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects7.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects8.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
docs/mock-gui/docs/img/mockgui_projects9.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/mock-gui/docs/img/mockgui_recip11.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
docs/mock-gui/docs/img/mockgui_recip2.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
docs/mock-gui/docs/img/mockgui_recip3.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
BIN
docs/mock-gui/docs/img/mockgui_recips1.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/mock-gui/docs/img/mockgui_rpm1.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
docs/mock-gui/docs/img/mockgui_rpm2.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/mock-gui/docs/img/mockgui_rpm3.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
42
docs/mock-gui/docs/index.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# MockGUI система сборки RPM пакетов
|
||||
|
||||
## Описание
|
||||
|
||||
Это однопользовательская система по сборке ПО, а так же по подготовке репозиториев для установки собранного ПО.
|
||||
|
||||
Система является надстройкой над git, mock, rpmbuild, т.е. использует данные команды для подготовки и сборки программ.
|
||||
|
||||
mock в отличие от rpmbuild позволяет собрать пакет, при этом не изменяя конфигурацию системы, где он собирается и очистка изменений затрагивает удаление всего одного каталога сборки проекта, а MockGUI позволяет через web интерфейс управлять сборками. Группировать их и готовить к публикации.
|
||||
|
||||
MockGUI содержит линейный список git проектов, которые позволяет сгруппировать в проекты.
|
||||
|
||||
Каждый проект является отдельным репозиторием, подписанный проект может быть доступен для использования в локальной сети.
|
||||
|
||||
|
||||
## Где можно запустить
|
||||
|
||||
ПО может быть установлено на следующих ОС:
|
||||
|
||||
* MSVSphere 9
|
||||
* Almalinux 9
|
||||
* RockyLinux 9
|
||||
|
||||
Требования к системе:
|
||||
|
||||
| | Минимальные требования |
|
||||
|--------|--------------------------------------------------------------|
|
||||
| CPU | 1 ядро |
|
||||
| Memory | Зависит от собираемых исходников, для небольших проектов 2Гб |
|
||||
| HDD | размер исходных кодов * 4 |
|
||||
|
||||
|
||||
## Интерфейс
|
||||
|
||||

|
||||
|
||||
## Исходные коды
|
||||
|
||||
* [github](https://github.com/bayrepo/mock-gui)
|
||||
* [developers git](https://dev.brepo.ru/brepo/mock-gui)
|
||||
|
||||
|
||||
86
docs/mock-gui/docs/install.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Способы установки
|
||||
|
||||
## С помощью ansible от пользователя root
|
||||
|
||||
```shell
|
||||
dnf install epel-release
|
||||
dnf install ansible git
|
||||
git clone https://dev.brepo.ru/brepo/mock-gui.git
|
||||
cd mock-gui/install
|
||||
ansible-galaxy install -r requirements.yml
|
||||
ansible-playbook mock-gui-install.yml --ask-become-pass
|
||||
перезагрузить систему
|
||||
systemctl enable mockgui
|
||||
systemctl start mockgui
|
||||
```
|
||||
|
||||
И обязательно задать пароль для mockgui, т.к. без ключа git будет запрашивать именно пароль для этого пользователя:
|
||||
|
||||
```shell
|
||||
passwd mockgui
|
||||
```
|
||||
|
||||
## С помощью ansible и пользователь в sudo
|
||||
|
||||
```shell
|
||||
sudo dnf install epel-release
|
||||
sudo dnf install ansible git
|
||||
git clone https://dev.brepo.ru/brepo/mock-gui.git
|
||||
cd mock-gui/install
|
||||
ansible-galaxy install -r requirements.yml
|
||||
ansible-playbook mock-gui-install.yml --ask-become-pass
|
||||
перезагрузить систему
|
||||
sudo systemctl enable mockgui
|
||||
sudo systemctl start mockgui
|
||||
```
|
||||
|
||||
И обязательно задать пароль для mockgui, т.к. без ключа git будет запрашивать именно пароль для этого пользователя:
|
||||
|
||||
```shell
|
||||
passwd mockgui
|
||||
```
|
||||
|
||||
## Ручная установка
|
||||
|
||||
Команды ниже выполнять под root или привилегированным пользователем с sudo:
|
||||
|
||||
1. отключить selinux
|
||||
2. `systemctl stop firewalld`
|
||||
3. `systemctl disable firewalld`
|
||||
4. `systemctl stop nftables`
|
||||
5. `systemctl disable nftables`
|
||||
6. `useradd mockgui`
|
||||
7. Добавить репозиторий:
|
||||
```ini
|
||||
# cat /etc/yum.repos.d/brepo_projects.repo
|
||||
[brepo_projects]
|
||||
name=msvsphere9 repo on repo.brepo.ru
|
||||
baseurl=https://repo.brepo.ru/hestia/
|
||||
enabled=1
|
||||
gpgkey=https://repo.brepo.ru/hestia/brepo_projects-gpg-key
|
||||
gpgcheck=1
|
||||
```
|
||||
8. `dnf install epel-release`
|
||||
9. `dnf install mock rpmdevtools rpm-build ccache rpm-sign sqlite sqlite-devel alt-brepo-ruby33 openssh-server git tar gcc gcc-c++ make cmake alt-brepo-ruby33-devel openssl-devel zlib-devel`
|
||||
10. `usermod -a -G mock mockgui`
|
||||
11. добавить в .bashrc root и mockgui строки: `export PATH=/usr/lib64/ccache:$PATH`
|
||||
|
||||
Команды ниже выполнять под пользователем mockgui:
|
||||
|
||||
|
||||
12. `cd ~`
|
||||
13. `git clone https://dev.brepo.ru/brepo/mock-gui.git`
|
||||
14. `cd mock-gui`
|
||||
15. `/opt/brepo/ruby33/bin/bundle install`
|
||||
16. `/opt/brepo/ruby33/bin/bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3`
|
||||
|
||||
|
||||
Следующая команда от root:
|
||||
|
||||
17. `cp /home/mockgui/mock-gui/mockgui.service /etc/systemd/system/mockgui.service`
|
||||
18. `systemctl enable mockgui.service --now`
|
||||
|
||||
И обязательно задать пароль для mockgui, т.к. без ключа git будет запрашивать именно пароль для этого пользователя:
|
||||
|
||||
19. `passwd mockgui`
|
||||
|
||||
31
docs/mock-gui/docs/run.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Запуск приложения
|
||||
|
||||
Для запуска необходимо выполнить следующую команду:
|
||||
|
||||
```shell
|
||||
sudo systemctl start mockgui
|
||||
```
|
||||
|
||||
## Ручной запуск без service файла
|
||||
|
||||
Ручной запуск без service файла может выполняться от пользователя состоящего в группе `mock`.
|
||||
|
||||
Подготовка базы данных(делается один раз):
|
||||
|
||||
```shell
|
||||
/opt/brepo/ruby33/bin/bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3
|
||||
```
|
||||
|
||||
Запуск приложения
|
||||
|
||||
```shell
|
||||
/opt/brepo/ruby33/bin/bundle exec /opt/brepo/ruby33/bin/ruby app.rb
|
||||
```
|
||||
|
||||
## Использование приложения
|
||||
|
||||
Открыть в браузере страницу:
|
||||
|
||||
```
|
||||
http://[ip]:8081
|
||||
```
|
||||
23
docs/mock-gui/docs/update.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Обновление
|
||||
|
||||
Для обновления сервиса, необходимо перейти в корневой каталог программы под пользователем mockgui:
|
||||
|
||||
```shell
|
||||
cd /home/mockgui/mock-gui/
|
||||
```
|
||||
|
||||
И затем вызвать команду:
|
||||
|
||||
```shell
|
||||
git pull
|
||||
```
|
||||
|
||||
Данна команда обновит системные каталоги, не тронув каталоги с данными.
|
||||
|
||||
И затем от привилегированного пользователя вызвать:
|
||||
|
||||
```shell
|
||||
sudo systemctl restart mockgui.service
|
||||
```
|
||||
|
||||
Единственное предупреждение - в момент обновления не должно происходить никаких сборок в MockGUI, они должны быть все завершенные.
|
||||
23
docs/mock-gui/docs/userguide/builds.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Список сборок
|
||||
|
||||
На данной странице отображается информация о текущих и прошлых сборках
|
||||
|
||||

|
||||
|
||||
Если нажать на ссылку "Детальнее", то можно получить детальную информацию о сборке:
|
||||
|
||||

|
||||
|
||||
Где можно увидеть все файлы логов с ошибками и без, пакеты собранные при сборке и т.д.
|
||||
|
||||
Сборка может находится в таком состоянии:
|
||||
* Идет сборка - проект собирается
|
||||
* Ожидает сборки - одновременно может происходить только одна сборка, поэтому все проекты требующие сборки будут ожидать очереди. В этот момент сборку можно отменить
|
||||
* Отменена - сборка была запланирована, но потом отменена
|
||||
* Ошибка сборки - сборка завершилась с ошибкой
|
||||
* Сборка успешно завершена - успешная сборка с пакетами
|
||||
|
||||
Список сборок может быть:
|
||||
* общим
|
||||
* сборки проекта
|
||||
* сборки проекта и git репозитория
|
||||
75
docs/mock-gui/docs/userguide/configuration.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Конфигурация
|
||||
|
||||
Конфигурация MockGUI представлена одним файлом `config.ini` расположенном в корне проекта.
|
||||
|
||||
## Пример файла конфигруации
|
||||
|
||||
```ini
|
||||
[server]
|
||||
port = 8081
|
||||
db = "sqlite://db/workbase.sqlite3"
|
||||
|
||||
[repo]
|
||||
repo = "repo"
|
||||
|
||||
[projects]
|
||||
path = "projects"
|
||||
old = 3
|
||||
|
||||
[counter]
|
||||
path = "locks/counter"
|
||||
|
||||
[configs]
|
||||
hide=open,amazon,anolis,circle,custom,euro,fedora,mageia,navy,alma,rocky
|
||||
selected=msvsphere
|
||||
|
||||
[pages]
|
||||
items_per_page = 30
|
||||
|
||||
[sign]
|
||||
path = "keys"
|
||||
|
||||
[repoview]
|
||||
path = "repoview"
|
||||
```
|
||||
|
||||
## Секции
|
||||
|
||||
Все пути указываются от корня проекта.
|
||||
|
||||
### server
|
||||
|
||||
* `port` - порт, который будет слушать сервер для доступа к WEB интерфейсу (умолчание: 8081)
|
||||
* `db` - путь к базе данных и тип базы данных (умолчание: db/workbase.sqlite3 и тип базы данных SQLite)
|
||||
|
||||
### repo
|
||||
|
||||
* `repo` - путь к каталогу, где гранятся bare git проекты, можно скопировать уже существующие, они автоматом при старте добавятся в базу (умолчание: repo)
|
||||
|
||||
### counter
|
||||
|
||||
* `path` - путь к глобальному счетчику сборок (умолчание: locks/counter)
|
||||
|
||||
### projects
|
||||
|
||||
* `path` - путь к папке, где создаются проекты, в нее же копируются git исходники, поэтому рекомендуется эту папку делать пообъемнее (умолчание: projects)
|
||||
* `old` - неиспользуемый параметр
|
||||
|
||||
### configs
|
||||
|
||||
* `hide` - скрыть из списка доступных конфигураций сборки считанных из `/etc/mock/`, содержащие в имени одно из слов, указанных через запятую
|
||||
* `selected` - добавить в список избранных сборки с именами, указанными содержащими слова через запятую
|
||||
|
||||
### pages
|
||||
|
||||
* `items_per_page` - отображать на страницах не более указанного числа записей (умолчание: 30)
|
||||
|
||||
### sign
|
||||
|
||||
* `path` - папка, где хранятся gpg ключи (приватный и публичный) для подписи пакетов (умолчание: keys)
|
||||
|
||||
### repoview
|
||||
|
||||
* `path` - папка, где хранятся шаблоны для генерации статического repoview для подписанного репозитория (умолчание: repoview)
|
||||
|
||||
Так же следует учесть, что сборки происходят в папке `/var/lib/mock` поэтому данная папка так же должна быть большого объема.
|
||||
19
docs/mock-gui/docs/userguide/env.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Окружения сборки mock
|
||||
|
||||
Отображает список доступных окружений, отфильтрованных согласно [конфигурации](configuration.md)
|
||||
|
||||
Файлы конфигурации доступны только для ознакомления.
|
||||
|
||||
Добавлять можно только вручную, редактируя файлы по пути `/etc/mock/`
|
||||
|
||||
## Список конфигураций
|
||||
|
||||

|
||||
|
||||
## Просмотр файлов конфигураций сборки
|
||||
|
||||

|
||||
|
||||
В листинге файла конфигурации сборки можно кликать мышкой на `include` и будет происходить переход на указанный шаблон или конфигурацию.
|
||||
|
||||
Такой механизм для ознакомления с конфигурацией сборки.
|
||||
51
docs/mock-gui/docs/userguide/gits.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Управление git репозиториями
|
||||
|
||||
По-умолчанию git проекты - это папки содержащие bare git структуры, которые расположены в каталоге `repo` (настраивается в [конфигурации](configuration.md))
|
||||
|
||||
Папка repo может содержать не зарегистрированные git репозитории, в таком случае они при открытии страницы автоматически добавятся в базу (правда без писания).
|
||||
|
||||
## Список git репозиториями
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Создание нового пустого git репозитория
|
||||
|
||||

|
||||
|
||||
Необходимо заполнить имя git репозитория и описание. И нажать кнопку "Создать".
|
||||
|
||||
## Просмотр информации git репозитория
|
||||
|
||||
На текущий момент есть возможность просмотра:
|
||||
|
||||
* Списка веток
|
||||
* Списка коммитов
|
||||
* Списка тегов
|
||||
* Строки для клонирования репозитория
|
||||
* Описание репозитория
|
||||
* Списка файлов указанной ветки репозитория
|
||||
|
||||
Список тегов и коммитов даст информацию о том, что исходные коды в ветке master находятся в нужно состоянии ис нужными коммитами.
|
||||
Сборочница производит выборку исходных кодов из master ветки git репозитория.
|
||||
|
||||

|
||||
|
||||
Пример строки клонирования репозитория:
|
||||
|
||||

|
||||
|
||||
Для успешного клонирования и наполнения git репозитория, необходимо либо установить публичный ключ для доступа пользователя по ssh или знать пароль к mockgui пользователю по ssh. Вот почему в [инсталляционном руководстве](../install.md) рекомендуется установить пароль для mockgui пользователя.
|
||||
|
||||
Вкладка отображения списка файлов репозитория и указанной ветки или HEAD:
|
||||
|
||||

|
||||
|
||||
## Удаление git репозитория
|
||||
|
||||
Удаление возможно только в том случае, если данный git репозиторий не имеет ни рецептов сборки, а так же не подключен ни к одному проекту и соответственно, не имеет сборок, указывающих на данный git репозиторий.
|
||||
|
||||
При нажатии на кнопку "Удалить" появится окно, где нужно будет написать имя репозитория и нажать кнопку "Удалить" для подтверждения удаления:
|
||||
|
||||

|
||||
173
docs/mock-gui/docs/userguide/projects.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Управление проектами
|
||||
|
||||
Проекты - это объединения git репозиториев, результат которого - это репозиторий rpm пакетов (подписанный и не подписанный).
|
||||
|
||||
Неподписанный репозиторий используется для внутренних сборок, при подключении проекта в другой проект.
|
||||
|
||||
Подписанный репозиторий используется для публикации rpm пакетов на внешний сервер (внешний репозиторий).
|
||||
|
||||
## Список проектов
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Создание нового проекта
|
||||
|
||||

|
||||
|
||||
* `Название проекта` - уникальное название проекта, оно будет содержаться в названии будущего репозитория, поэтому стоит подходить к названию с умом.
|
||||
* `Описание` - краткое описание
|
||||
* `Не публиковать отладочные пакеты и исходные коды` - при установленном флажке в подписанный репозиторий не публикуются пакеты src.rpm, debuginfo, debugsource. Если флажок не установлен, то подписывается и публикуется все. Флажок для проприетарного ПО
|
||||
* `Добавлять к версии пакетов дату и ID сборки` - при установке данного флажка, все сборки проекта в версии пакета, т.е. все пакеты будут иметь суффикс вида `YYYY-MM-DD_BUILDID`. Т.е. если из гит репозитория из spec файла собирается пакет с версией `0.1-1`, то в таком проекте будет собран пакет с версией `0.1-1.20251010_10`, при слудующей сборке даже без изменения spec файла будет собран пакет с версией `0.1-1.20251010_11` и т.д. Такие проекты удобны для создания тестовых репозиториев, когда не нужны изменения в spec файле, но функционал собираемой программы меняется.
|
||||
* `Выберите конфигурацию окружения сборки для проекта` - выбирается конфигурация сборки, выбирается один раз при создании проекта и больше потом не меняется. Можно только создать новый проект с другой конфигурацией.
|
||||
|
||||
Пример выбора конфигурации. Имеется поле фильтрации.
|
||||
|
||||

|
||||
|
||||
## Информация о проекте
|
||||
|
||||

|
||||
|
||||
`Добавить git репозиторий к проекту` - позволяет добавить к проекту один из существующих git репозиториев.
|
||||
|
||||
Необходимо из выпадающего списка выбрать один - нажать кнопку "Добавить".
|
||||
|
||||
Тк выглядит добавленный в проект новый git репозиторий.
|
||||

|
||||
|
||||
Пустые репозиторий без исходников добавить в проект нельзя!
|
||||
|
||||
`Редактировать конфигурацию сборки` - локальная конфигурация сборочного окружения для mock.
|
||||
|
||||
Данный пункт меню позволяет отредактировать корневой файл сборочного окружения.
|
||||
Здесь условно можно поменять конфигурацию сборки с которой создавался проект, но такой финт лучше не проделывать, особенно если есть уже собранные пакеты.
|
||||
|
||||

|
||||
|
||||
`Добавить внутренний репозиторий из другого проекта` - позволяет связать проекты при сборке, т.е внутренний неподписанный репозиторий одного проекта, становится доступен для сборки текущего.
|
||||
|
||||

|
||||
|
||||
`Список всех пакетов` - список пакетов, собранных для данного проекта (неподписанных пакетов)
|
||||
|
||||

|
||||
|
||||
Отображается структура репозитория, где можно перемещаться по папкам и нажимать на rpm пакеты, получая о них информацию.
|
||||
|
||||
`Управление снимками репозитория` - это ссылка настраницу, где можно создать снимок `неопубликоанного` репозитория, восстановить пакеты из сохраненного писка или удалить ранее созданные снимки или посмотреть содержимое снимков.
|
||||
|
||||
Данная опция полезна при деструктивных изменениях в проекте, например собирается пакет с сомнительным функционалом и лучше перед сборкой создать снимок, чтоб можно было потом откатить изменения обратно.
|
||||
|
||||
Пример создания и управления снимками.
|
||||
|
||||
Так выглядит страница управления снимками:
|
||||
|
||||

|
||||
|
||||
Слева список снимков, справа - содержимое выбранного снимка.
|
||||
|
||||
Пример отображения содержимого:
|
||||
|
||||

|
||||
|
||||
Напротив каждого снимка есть занчок "Корзина" и "Стрелочка". Нажимая на корзинку - можно удалить снимок, а на жимая на стрелочку - восстановить пакеты из снимка. При восстановлении, пакеты из неподписанного репозитория удаляются и заменяются пакетами из снимка. Далее их можно переподписать и они переместятся в репозиторий с подписанными пакетами.
|
||||
|
||||
Для создания снимка необхоимо нажать на знак "+".
|
||||
|
||||
Важно отметить, что для создания, удаления или восстановления снимка открывается дополнительный экран:
|
||||
|
||||

|
||||
|
||||
Для подтвержения действия недостаточно только нажать на кнопку "Создать", "Удалить", "Восстановить". Важно еще сделать активным флажок.
|
||||
|
||||
Этот флажок подтверждает действие окончательно. Если его не установить, то ничего не произойдет и операция не будет выполнена.
|
||||
|
||||
`Список сборок проектов` - отображается список сборок, принадлежащих проекту.
|
||||
|
||||
В данном списке можно получить информацию о каждой сборке. Детальнее о [сборках](builds.md)
|
||||
|
||||
`Подписать` - подписать gpg ключом файлы репозитория проекта и опубликовать их в отдельном репозитории
|
||||
|
||||
Как происходит подпись:
|
||||
|
||||

|
||||
|
||||
Подписывание требует наличие gpg ключей в папке keys.
|
||||
|
||||
Данные ключи создаются при [установке](../install.md) скриптом ./install-key из папки `gen-scripts`
|
||||
|
||||
Параметры запуска:
|
||||
|
||||
```shell
|
||||
cd gen-scripts
|
||||
./install-key UserName UserEmail 316224000 StrongSignPassword
|
||||
```
|
||||
|
||||
* UserName - это имя владельца ключа
|
||||
* UserEmail - почта владельца ключа
|
||||
* 316224000 - число секунд жизни ключа. Высчитывается по формуле: пусть нужен ключ на 2 года значит число будет: 2 * 366 * 24 * 60 * 60 = 63244800, а 316224000 = 10 лет
|
||||
* StrongSignPassword - пароль для ключа
|
||||
|
||||
В результате будет сгенерирован gpg ключ приватный и публичный в папке keys, желательно не потерять их.
|
||||
|
||||
`Просмотр подписанного репозитория` - после подписи в проекте появится подписанный репозиторий с пакетами и сгенерированным файлом repoview. Т.е в таком виде как репозиторий будет выглядеть при публикации в интернете.
|
||||
|
||||

|
||||
|
||||
`Установить адрес подписанного репозитория` - установить адрес подписанного репозитория, т.е установить url, данная информация необходима для корректного формирования шапки:
|
||||
|
||||
```shell
|
||||
echo -e "[project_test1]
|
||||
name=project_test1
|
||||
baseurl=https://test.repo.tst/repo1/
|
||||
enabled=1
|
||||
gpgkey=https://test.repo.tst/repo1/project_test1-gpg-key
|
||||
gpgcheck=1" > /etc/yum.repos.d/project_test1.repo
|
||||
```
|
||||
|
||||
т.е устанавливается корректный url будущего удаленного репозитория.
|
||||
|
||||
`Удалить старые версии пакетов` - позволяет подчистить накопившиеся старые версии пакетов, остаются только заданное число самых свежив версий.
|
||||
При нажатии, так же нужно подтвердить намерение удаления и указать, число версий пакета, которые должны быть оставлены. Но нужно быть аккуратным, например некоторые пакеты требуют наличие более старых версий при сборке.
|
||||
Именно для этого, перед удалением выдается сисок всех пакетов, которые планируются к удалению. И в данном списке можно отменить удаление отдельных пакетов.
|
||||
|
||||
|
||||
|
||||
`Удалить проект` - удаляется весь проект, подписанные и неподписанные репозитории, сборки.
|
||||
|
||||
Но если проект залинкован в другом проекте, то удаление будет невозможным, пока не будет снят линк.
|
||||
|
||||

|
||||
|
||||
Для удаления - нужно перевести в активное состояние флажок и нажать кнопку "Удалить".
|
||||
Без установки флажка - удаление не произойдет. Такая защита от случайного удаления.
|
||||
|
||||
### Список действий над репозиторием проекта
|
||||
|
||||
Напротив каждого git репозитория, добавленного в проект есть список действий:
|
||||
|
||||

|
||||
|
||||
Слева направо:
|
||||
* удалить git репозиторий из проекта (при этом rpm пакеты в проекте, сборки - остаются)
|
||||
* обновить исходники проекта - если такой значок появился, значит в git репозиторий в ветку master были внесены изменения и в текущем проекте неактуальные исходники. Когда исходники актуальны список действий выглядит так:
|
||||
|
||||

|
||||
|
||||
* запустить сборку - запускается сборка проекта в фоне. Отображается страница, которая обновляется каждые 5 секунд (обновление можно остановить или запустить опять)
|
||||
|
||||

|
||||
|
||||
Во время сборки отображается лог файл процесса сборки. Результат сборки можно посмотреть на странице [сборки](builds.md)
|
||||
|
||||
* указать файл spec для проекта и git репозитория - если в гит репозитории несколько spec файлов, то будет подхватываться первый попавшийся файл, чтоб избежать такого поведения можно точно указать какой файл использовать
|
||||
|
||||

|
||||
|
||||
* отобразить список сборок для проекта и текущего git репозитория
|
||||
|
||||
Если нажать на имя git репозитория в списке, то можно просмотреть его содержимое, т.е список файлов:
|
||||
|
||||

|
||||
82
docs/mock-gui/docs/userguide/recips.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Сценарии подготовки исходных кодов git репозиториев
|
||||
|
||||
Сценарии подготовки исходных кодов git репозиториев в дальнейшем буду называть `рецептами`.
|
||||
|
||||
Для сборки необходимо, чтоб все файлы git проекта были прописаны в spec файле, а зачастую в spec файле файлы исходных кодов прописаны как один архив.
|
||||
|
||||
По умолчанию MockGUI предполагает, что в spec прописаны все исходные файлы, которые используются при сборке и ничего делать не нужно.
|
||||
|
||||
Но если необходимо проделать какие-то манипуляции для подготовки исходных файлов, то сборка завершится с ошибкой.
|
||||
|
||||
Чтоб не было ошибки для этого сделаны рецепты.
|
||||
|
||||
Рецепт - это по сути bash скрипт, который:
|
||||
|
||||
* получает на вход spec файл
|
||||
* парсит spec файл при необходимости
|
||||
* делает необходимые манипуляции с исходными кодами
|
||||
* производит подготовку к сборке
|
||||
|
||||
Нужно учитывать при написании сценариев, что они выполняются в реальной системе, поэтому их функционал ограничен. Например это может быть:
|
||||
|
||||
* использование sed, grep для создания файлов конфигураций
|
||||
* использование доступных архиватор в системе для подготовки архива
|
||||
* и т.д.
|
||||
|
||||
Пример такого скрипта уже встроен в MockGUI - make_tar_from_git. Он из исходных кодов git проекта создает архив для сборки:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
need_spec="n"
|
||||
SPEC="$1"
|
||||
FIND_SPEC="$SPEC"
|
||||
if [ -z "$SPEC" ];then
|
||||
need_spec="y"
|
||||
fi
|
||||
if [ -n "$SPEC" -a ! -e "$SPEC" ];then
|
||||
need_spec="y"
|
||||
fi
|
||||
if [ "$need_spec" == "y" ];then
|
||||
FIND_SPEC=$(/usr/bin/find . -iname "*.spec" -type f -print -quit)
|
||||
fi
|
||||
if [ -n "$FIND_SPEC" ];then
|
||||
NAME=$(rpm -q --queryformat="%{NAME}" --specfile "$FIND_SPEC" | xargs)
|
||||
VERSION=$(rpm -q --queryformat="%{VERSION}" --specfile "$FIND_SPEC" | xargs)
|
||||
PKG_NAME="${NAME}-${VERSION}"
|
||||
tar -h --exclude="${PKG_NAME}.tar.gz" --exclude=".git" --exclude="$FIND_SPEC" -cvf ${PKG_NAME}.tar.gz --transform "s,^,${PKG_NAME}/," *
|
||||
exit 0
|
||||
else
|
||||
echo "Не найден spec файл"
|
||||
exit 255
|
||||
fi
|
||||
```
|
||||
|
||||
Его код прост, скрипт выполняется в корне git репозитория, он получает spec файл, извлекает из него версию и имя пакета и создает тут же архив.
|
||||
Данные рецепты могут изменять исходники, это не вредит репозиторию, т.к все манипуляции делаются с копией данных в временном каталоге.
|
||||
|
||||
## Получить список рецептов
|
||||
|
||||

|
||||
|
||||
## Добавить новый рецепт
|
||||
|
||||

|
||||
|
||||
`Имя рецепта` - имя файла, который будет создаваться при подготовке сборки во временных исходниках проекта. Имя желательно выбирать такое, чтоб оно не переписало файл исходников
|
||||
|
||||
`Описанье` - краткое описание подготовительного скрипта
|
||||
|
||||
`Код` - bash скрипт
|
||||
|
||||
`git проекты использующие рецепт` - список git-репозиториев, для которых данный рецепт будет применяться. Может иметь несколько значений
|
||||
|
||||
## Редактирование существующего рецепта
|
||||
|
||||

|
||||
|
||||
## Удаление рецепта
|
||||
|
||||

|
||||
|
||||
Для удаления, нужно нажать кнопку "Удалить" и в появившемся окне вписать имя рецепта для подтверждения удаления.
|
||||
13
docs/mock-gui/docs/userguide/rpms.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Список пакетов
|
||||
|
||||
В данном разделе отображается список git репозиториев и число пакетов собранных из данного репозитория
|
||||
|
||||

|
||||
|
||||
Для получения более детальной информации нужно нажать на стрелку, и отобразится список всех пакетов, собранных из данного репозитория.
|
||||
|
||||

|
||||
|
||||
Здесь же можно получить информацию когда и для какого проекта собирался пакет, а так же детальную информацию об rpm пакете.
|
||||
|
||||

|
||||
25
docs/mock-gui/mkdocs.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
site_name: MockGUI
|
||||
site_url: https://docs.brepo.ru/mockgui/
|
||||
nav:
|
||||
- index.md
|
||||
- install.md
|
||||
- update.md
|
||||
- run.md
|
||||
- docs.md
|
||||
- Инструкция пользователя:
|
||||
- common.md
|
||||
- userguide/configuration.md
|
||||
- userguide/gits.md
|
||||
- userguide/recips.md
|
||||
- userguide/env.md
|
||||
- userguide/projects.md
|
||||
- userguide/builds.md
|
||||
- userguide/rpms.md
|
||||
theme:
|
||||
name: readthedocs
|
||||
locale: ru
|
||||
extra:
|
||||
generator: false
|
||||
copyright: Copyright © 2025 - ... BayRepo
|
||||
repo_url: https://dev.brepo.ru/brepo/mock-gui
|
||||
|
||||
123
install/mock-gui-install.yml
Normal file
@@ -0,0 +1,123 @@
|
||||
- name: Install MockGUI
|
||||
hosts: localhost
|
||||
connection: local
|
||||
become: True
|
||||
become_user: root
|
||||
become_method: su
|
||||
tasks:
|
||||
- name: Disable SELinux
|
||||
ansible.posix.selinux:
|
||||
state: disabled
|
||||
|
||||
- name: Stop and disable firewalld
|
||||
ansible.builtin.service:
|
||||
name: firewalld
|
||||
state: stopped
|
||||
enabled: false
|
||||
|
||||
- name: Stop and disable nftables
|
||||
ansible.builtin.service:
|
||||
name: nftables
|
||||
state: stopped
|
||||
enabled: false
|
||||
|
||||
- name: Add mockgui user
|
||||
ansible.builtin.user:
|
||||
name: mockgui
|
||||
create_home: yes
|
||||
home: /home/mockgui
|
||||
|
||||
- name: Add mockgui repo file
|
||||
ansible.builtin.copy:
|
||||
dest: "/etc/yum.repos.d/brepo_projects.repo"
|
||||
content: |
|
||||
[brepo_projects]
|
||||
name=msvsphere9 repo on repo.brepo.ru
|
||||
baseurl=https://repo.brepo.ru/hestia/
|
||||
enabled=1
|
||||
gpgkey=https://repo.brepo.ru/hestia/brepo_projects-gpg-key
|
||||
gpgcheck=1
|
||||
|
||||
- name: Install epel repository
|
||||
ansible.builtin.dnf:
|
||||
name: epel-release
|
||||
state: present
|
||||
|
||||
- name: Install other needed packages
|
||||
ansible.builtin.dnf:
|
||||
name:
|
||||
- mock
|
||||
- rpmdevtools
|
||||
- rpm-build
|
||||
- ccache
|
||||
- rpm-sign
|
||||
- sqlite
|
||||
- sqlite-devel
|
||||
- alt-brepo-ruby33
|
||||
- openssh-server
|
||||
- git
|
||||
- tar
|
||||
- gcc
|
||||
- gcc-c++
|
||||
- make
|
||||
- cmake
|
||||
- alt-brepo-ruby33-devel
|
||||
- openssl-devel
|
||||
- zlib-devel
|
||||
state: present
|
||||
|
||||
- name: Add mockgui to mock
|
||||
ansible.builtin.user:
|
||||
name: mockgui
|
||||
groups: mock
|
||||
append: yes
|
||||
|
||||
- name: Clone project to the mockgui
|
||||
ansible.builtin.git:
|
||||
repo: https://dev.brepo.ru/brepo/mock-gui.git
|
||||
dest: /home/mockgui/mock-gui
|
||||
single_branch: yes
|
||||
version: master
|
||||
ignore_errors: true
|
||||
|
||||
- name: Add ccache to PATH
|
||||
ansible.builtin.lineinfile:
|
||||
path: /home/mockgui/.bashrc
|
||||
insertbefore: '^export PATH'
|
||||
line: 'export PATH=/usr/lib64/ccache:$PATH'
|
||||
|
||||
- name: Change sources owner
|
||||
ansible.builtin.file:
|
||||
path: /home/mockgui/mock-gui
|
||||
state: directory
|
||||
recurse: yes
|
||||
owner: mockgui
|
||||
group: mockgui
|
||||
|
||||
- name: Install needed ruby gems
|
||||
ansible.builtin.command: su - mockgui -c "cd /home/mockgui/mock-gui; /opt/brepo/ruby33/bin/bundle install"
|
||||
args:
|
||||
chdir: /home/mockgui/mock-gui
|
||||
register: myout
|
||||
changed_when: myout.rc == 0
|
||||
failed_when: myout.rc != 0
|
||||
|
||||
|
||||
- name: Create database
|
||||
ansible.builtin.command: su - mockgui -c "cd /home/mockgui/mock-gui; /opt/brepo/ruby33/bin/bundle exec sequel -m db/migrations sqlite://db/workbase.sqlite3"
|
||||
args:
|
||||
chdir: /home/mockgui/mock-gui
|
||||
register: myout
|
||||
changed_when: myout.rc == 0
|
||||
failed_when: myout.rc != 0
|
||||
|
||||
|
||||
- name: Copy systemd service file to server
|
||||
ansible.builtin.copy:
|
||||
remote_src: true
|
||||
src: /home/mockgui/mock-gui/mockgui.service
|
||||
dest: /etc/systemd/system
|
||||
owner: root
|
||||
group: root
|
||||
mode: 0644
|
||||
|
||||
3
install/requirements.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
collections:
|
||||
- ansible.posix
|
||||
0
locks/build
Normal file
0
logs/empty
Normal file
19
mockgui.service
Normal file
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=MockGUI application server
|
||||
After=syslog.target network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=mockgui
|
||||
Group=mockgui
|
||||
Environment=RACK_ENV=production
|
||||
WorkingDirectory=/home/mockgui/mock-gui
|
||||
ExecStart=/opt/brepo/ruby33/bin/bundle exec /opt/brepo/ruby33/bin/ruby app.rb
|
||||
#UMask=0002
|
||||
RestartSec=1
|
||||
Restart=on-failure
|
||||
SyslogIdentifier=mockgui_server
|
||||
LimitNOFILE=15360
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
0
projects/empty
Normal file
0
repo/empty
Normal file
7
repoview/bootstrap.bundle.min.js
vendored
Normal file
6
repoview/bootstrap.min.css
vendored
Normal file
122
repoview/template.erb
Normal file
@@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<title>
|
||||
Список достпуных в репозитории пакетов
|
||||
</title>
|
||||
<link rel="stylesheet" href="bootstrap.min.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container p-3">
|
||||
<p>Для подключения репозитория в своей системе используейте команду</p>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<pre>
|
||||
echo -e "[<%= repo_name %>]
|
||||
name=<%= repo_name %>
|
||||
skip_if_unavailable=True
|
||||
baseurl=<%= repo_url %>
|
||||
enabled=1
|
||||
gpgkey=<%= repo_url %><%= repo_name %>-gpg-key
|
||||
gpgcheck=1" > /etc/yum.repos.d/<%= repo_name %>.repo
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container p-3">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Последние обновленные пакеты</h5>
|
||||
<h6 class="card-subtitle mb-2 text-body-secondary">src.rpm (для непубличных репозиториев сам пакет с исходными кодами может отсутствовать)</h6>
|
||||
<p class="card-text">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Дата обновления</th>
|
||||
<th scope="col">Пакет исходников</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<% last_update_src.each do |item| %>
|
||||
<tr>
|
||||
<td><%= item[0] %></td>
|
||||
<td><%= item[1] %></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container p-3">
|
||||
<div class="card border-warning mb-3">
|
||||
<div class="card-header">Список доступных в репозитории пакетов</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title text-danger-emphasis">Всего пакетов <%= pkg_num %></h5>
|
||||
<div class="accordion" id="dirInfo">
|
||||
<% repo_data.each_with_index do |item, index| %>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
|
||||
data-bs-target="#id<%= index %>" aria-expanded="false" aria-controls="id<%= index %>">
|
||||
<%= item[1] %>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="id<%= index %>" class="accordion-collapse collapse" data-bs-parent="#dirInfo">
|
||||
<div class="accordion-body">
|
||||
<div class="accordion accordion-flush" id="rpmInfo">
|
||||
<% item[0].each_with_index do |idata, jndex| %>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button"
|
||||
data-bs-toggle="collapse" data-bs-target="#rid<%= jndex %>" aria-expanded="false"
|
||||
aria-controls="rid<%= jndex %>">
|
||||
<%= idata[1] %>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="rid<%= jndex %>" class="accordion-collapse collapse" data-bs-parent="#rpmInfo">
|
||||
<div class="accordion-body">
|
||||
|
||||
<div class="accordion accordion-flush" id="pkgInfo">
|
||||
<% idata[0].each_with_index do |iidata, ijndex| %>
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#pid<%= ijndex %>" aria-expanded="false" aria-controls="pid<%= ijndex %>">
|
||||
<%= iidata[:fname] %> (<%= iidata[:stat]%>)
|
||||
</button>
|
||||
</h2>
|
||||
<div id="pid<%= ijndex %>" class="accordion-collapse collapse" data-bs-parent="#pkgInfo" style="">
|
||||
<div class="accordion-body">
|
||||
<div class="accordion accordion-flush" id="pkgInfo">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item list-group-item-warning"> Скачать пакет - <a href="<%= iidata[:aname] %>"><%= iidata[:fname] %></a> </li>
|
||||
<% iidata[:chlog].each do |chlg| %>
|
||||
<li class="list-group-item list-group-item-warning"> <%= chlg.text %> </li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
23
views/buildsclean.erb
Normal file
@@ -0,0 +1,23 @@
|
||||
<%= erb :header %>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4"></div>
|
||||
<div class="col-md-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Подчистка окружений сборок произошла успешно</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Ни один проект не был очищен, очищены только окружения сборок. Это значит, что следующая сборка будет длится дольше, но некоторые ошибки накопившиеся в кэше сборки будут устранены</p>
|
||||
<ul class="list-group">
|
||||
<% @list_cleaned.each do |item| %>
|
||||
<li class="list-group-item"><%= item %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4"></div>
|
||||
</div>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||
@@ -8,6 +8,7 @@
|
||||
<th scope="col" class="text-center">git репозиторий</th>
|
||||
<th scope="col" class="text-center">Дата сборки</th>
|
||||
<th scope="col" class="text-center">Число собранных пакетов</th>
|
||||
<th scope="col" class="text-center">Время сборки</th>
|
||||
<th scope="col" class="text-center">Состояние</th>
|
||||
<th scope="col" class="text-center">Перейти к сборке</th>
|
||||
</tr>
|
||||
@@ -25,6 +26,12 @@
|
||||
when 2
|
||||
st = "Сборка успешно завершена"
|
||||
cl = "text-bg-success"
|
||||
when 3
|
||||
st = "Ожидает сборки"
|
||||
cl = "text-bg-secondary"
|
||||
when 4
|
||||
st = "Отменена"
|
||||
cl = "text-bg-light"
|
||||
else
|
||||
st = "Неизвестно"
|
||||
cl = "text-bg-light"
|
||||
@@ -36,10 +43,19 @@
|
||||
<td class="text-center"><a href="/prjgitf/<%= ERB::Util.url_encode(item[:projid]) %>/<%= ERB::Util.url_encode(item[:gitid]) %>"><%= item[:reponame] %></a></td>
|
||||
<td class="text-center"><%= item[:createat] %></td>
|
||||
<td class="text-center"><%= item[:pkgcnt] %></td>
|
||||
<td class="text-center"><%= item[:timeproc] %> сек</td>
|
||||
<td class="text-center <%= cl %>">
|
||||
<%= st %>
|
||||
</td>
|
||||
<td class="text-center"><a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a></td>
|
||||
<td class="text-center">
|
||||
<% if item[:state] == 3 %>
|
||||
<a href="/buildterm/<%= ERB::Util.url_encode(item[:buildid]) %>">Отменить</a>
|
||||
<% elsif item[:state] == 4 %>
|
||||
Нет информации
|
||||
<% else %>
|
||||
<a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item me-4"><a class="link-light" href="https://brepo.ru">Документация</a></li>
|
||||
<li class="list-inline-item me-4"><a class="link-light" href="https://dev.brepo.ru">Разработка</a></li>
|
||||
<li class="list-inline-item"><a class="link-light" href="https://hestiadocs.brepo.ru">Партнеры</a></li>
|
||||
<li class="list-inline-item"><a class="link-light" href="/sanitize">Подчистка</a></li>
|
||||
<li class="list-inline-item"><a class="link-light" href="/buildsclean">Сбросить buildroots</a></li>
|
||||
</ul>
|
||||
<ul class="list-inline">
|
||||
<li class="list-inline-item me-4"><a href="https://dev.brepo.ru"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-github text-light">
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<li class="nav-item" role="presentation"><a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-2">Список коммитов</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-3">Список тэгов</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-4">Клонировать</a></li>
|
||||
<li class="nav-item" role="presentation"><a class="nav-link" role="tab" data-bs-toggle="tab" href="#tab-5">Список файлов</a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" role="tabpanel" id="tab-1">
|
||||
@@ -123,6 +124,34 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" role="tabpanel" id="tab-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-start">
|
||||
<div class="col-10">
|
||||
<p style="font-weight: bold;"><%= @repo_data[:info][:reponame] %></p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row align-items-start">
|
||||
<div class="col-4" style="background: #d3e2ff;border-radius: 15px;padding-top: 20px;padding-bottom: 20px;">
|
||||
<p>
|
||||
<% if @repo_data[:info][:descr].nil? || @repo_data[:info][:descr] == "" %>
|
||||
"Нет описания"
|
||||
<% else %>
|
||||
<%= @repo_data[:info][:descr] %>
|
||||
<% end %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<p style="color: var(--bs-red);">Список файлов</p>
|
||||
<ul>
|
||||
<% @repo_data[:files].each do |tags| %>
|
||||
<li><%= tags %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,60 @@
|
||||
<%= erb :header %>
|
||||
|
||||
<div class="container">
|
||||
<h2 class="text-bg-danger text-center rounded-3 mb-3">Отсуствуют ключи для подписи</h2>
|
||||
<p class="text-danger">Важный момент - сохраните файл ~/.rpmmacros, т.к. он будет перезаписан командой <span
|
||||
class="text-success">install-key</span></p>
|
||||
<p>Для создания ключа, необходимо запустить скрипт <span class="text-success">install-key</span> из каталога
|
||||
gen-scripts</p>
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
Параметры запуска скрипта
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<blockquote class="blockquote mb-0">
|
||||
<p class="font-monospace">bash install-key UserName Email TimeToExpireInSecondsFromCurrentTime
|
||||
PhassPhrase</p>
|
||||
<footer class="blockquote-footer">
|
||||
<p>Где:</p>
|
||||
<ul>
|
||||
<li>UserName - имя вадельца ключа</li>
|
||||
<li>Email - почта владельца ключа</li>
|
||||
<li>TimeToExpireInSecondsFromCurrentTime - время в секундах до истечения срока давности
|
||||
ключа</li>
|
||||
<li>PhassPhrase - пароль для ключа</li>
|
||||
</ul>
|
||||
</footer>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<p>После генерации ключа пароль будет сохранен:</p>
|
||||
<div class="card text-bg-light mb-3">
|
||||
<div class="card-header">Сохранение пароля в файл (да, небезопасно, но пока так) в каталог keys</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">каталог keys/save</h5>
|
||||
<p class="card-text">Храните файл как зеницу ока!</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>Примеры генерации ключа:</p>
|
||||
<div class="card text-bg-light mb-3">
|
||||
<div class="card-header">Создание ключа на один год</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">В сеундах: 366 * 24 * 60 * 60 = 31622400</h5>
|
||||
<p class="card-text">./install-key RpmKeeper rpmkeeper@test.example.ru 31622400 12345678</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-bg-light mb-3">
|
||||
<div class="card-header">Создание ключа на 10 лет</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">В сеундах: 366 * 10 * 24 * 60 * 60 = 316224000</h5>
|
||||
<p class="card-text">./install-key RpmKeeper rpmkeeper@test.example.ru 316224000 12345678</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-bg-light mb-3">
|
||||
<div class="card-header">Создание ключа на 5 лет</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">В сеундах: 366 * 5 * 24 * 60 * 60 = 1581120000</h5>
|
||||
<p class="card-text">./install-key RpmKeeper rpmkeeper@test.example.ru 158112000 12345678</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||
@@ -45,6 +45,10 @@
|
||||
<span class="p-1">
|
||||
<a href="/bldcfg">build-окружения</a>
|
||||
</span>
|
||||
<span>|</span>
|
||||
<span class="p-1">
|
||||
<a href="/builds">сборки</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<th scope="col" class="text-center">git репозиторий</th>
|
||||
<th scope="col" class="text-center">Дата сборки</th>
|
||||
<th scope="col" class="text-center">Число собранных пакетов</th>
|
||||
<th scope="col" class="text-center">Время сборки</th>
|
||||
<th scope="col" class="text-center">Состояние</th>
|
||||
<th scope="col" class="text-center">Перейти к сборке</th>
|
||||
</tr>
|
||||
@@ -26,21 +27,36 @@
|
||||
when 2
|
||||
st = "Сборка успешно завершена"
|
||||
cl = "text-bg-success"
|
||||
when 3
|
||||
st = "Ожидает сборки"
|
||||
cl = "text-bg-secondary"
|
||||
when 4
|
||||
st = "Отменена"
|
||||
cl = "text-bg-light"
|
||||
else
|
||||
st = "Неизвестно"
|
||||
cl = "text-bg-light"
|
||||
end
|
||||
%>
|
||||
%> end
|
||||
<tr>
|
||||
<td scope="row"><%= item[:buildid] %></td>
|
||||
<td class="text-center"><a href="/prjedit/<%= ERB::Util.url_encode(item[:projid]) %>"><%= item[:prjname] %></a></td>
|
||||
<td class="text-center"><a href="/prjgitf/<%= ERB::Util.url_encode(item[:projid]) %>/<%= ERB::Util.url_encode(item[:gitid]) %>"><%= item[:reponame] %></a></td>
|
||||
<td class="text-center"><%= item[:createat] %></td>
|
||||
<td class="text-center"><%= item[:pkgcnt] %></td>
|
||||
<td class="text-center"><%= item[:timeproc] %> сек</td>
|
||||
<td class="text-center <%= cl %>">
|
||||
<%= st %>
|
||||
</td>
|
||||
<td class="text-center"><a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a></td>
|
||||
<td class="text-center">
|
||||
<% if item[:state] == 3 %>
|
||||
<a href="/buildterm/<%= ERB::Util.url_encode(item[:buildid]) %>">Отменить</a>
|
||||
<% elsif item[:state] == 4 %>
|
||||
Нет информации
|
||||
<% else %>
|
||||
<a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<th scope="col" class="text-center">git репозиторий</th>
|
||||
<th scope="col" class="text-center">Дата сборки</th>
|
||||
<th scope="col" class="text-center">Число собранных пакетов</th>
|
||||
<th scope="col" class="text-center">Время сборки</th>
|
||||
<th scope="col" class="text-center">Состояние</th>
|
||||
<th scope="col" class="text-center">Перейти к сборке</th>
|
||||
</tr>
|
||||
@@ -26,6 +27,12 @@
|
||||
when 2
|
||||
st = "Сборка успешно завершена"
|
||||
cl = "text-bg-success"
|
||||
when 3
|
||||
st = "Ожидает сборки"
|
||||
cl = "text-bg-secondary"
|
||||
when 4
|
||||
st = "Отменена"
|
||||
cl = "text-bg-light"
|
||||
else
|
||||
st = "Неизвестно"
|
||||
cl = "text-bg-light"
|
||||
@@ -37,10 +44,19 @@
|
||||
<td class="text-center"><a href="/prjgitf/<%= ERB::Util.url_encode(item[:projid]) %>/<%= ERB::Util.url_encode(item[:gitid]) %>"><%= item[:reponame] %></a></td>
|
||||
<td class="text-center"><%= item[:createat] %></td>
|
||||
<td class="text-center"><%= item[:pkgcnt] %></td>
|
||||
<td class="text-center"><%= item[:timeproc] %> сек</td>
|
||||
<td class="text-center <%= cl %>">
|
||||
<%= st %>
|
||||
</td>
|
||||
<td class="text-center"><a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a></td>
|
||||
<td class="text-center">
|
||||
<% if item[:state] == 3 %>
|
||||
<a href="/buildterm/<%= ERB::Util.url_encode(item[:buildid]) %>">Отменить</a>
|
||||
<% elsif item[:state] == 4 %>
|
||||
Нет информации
|
||||
<% else %>
|
||||
<a href="/buildinfof/<%= ERB::Util.url_encode(item[:buildid]) %>">Детальнее</a>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
|
||||
18
views/prjclean1.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<%= erb :header %>
|
||||
<div class="container">
|
||||
<h2 class="text-center">Удаление сатрых версий пакетов проекта <%= @proj_name %></h2>
|
||||
<form action="/prjclean/<%= ERB::Util.url_encode(@proj_id) %>" method="post">
|
||||
<div class="form-check form-switch text-center pb-3">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="isclean" name="isclean">
|
||||
<label class="form-check-label" for="isclean">
|
||||
Вы уверены, что хотите удалить старые версии пакетов?<br />
|
||||
Все старые версии пакетов проекта будут удалены, действие не подлежит отмене.
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3 text-center">
|
||||
<button type="submit" class="btn btn-primary" name="cancel" value="cancel">Отменить</button>
|
||||
<button type="submit" class="btn btn-danger" name="delete" value="delete">Удалить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||
18
views/prjclean2.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<%= erb :header %>
|
||||
<div class="container">
|
||||
<h2 class="text-center">Удаление сатрых версий пакетов проекта <%= @proj_name %></h2>
|
||||
<form action="/prjclean_step2/<%= ERB::Util.url_encode(@proj_id) %>" method="post">
|
||||
<div class="form-check form-switch text-center pb-3">
|
||||
<label for="inputvernum" class="form-label">Число версий</label>
|
||||
<input type="text" id="inputvernum" name="inputvernum" class="form-control" aria-describedby="vernumHelpBlock">
|
||||
<div id="vernumHelpBlock" class="form-text">
|
||||
Укажите число версий каждого пакета, которые должны остаться
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3 text-center">
|
||||
<button type="submit" class="btn btn-primary" name="cancel" value="cancel">Отменить</button>
|
||||
<button type="submit" class="btn btn-danger" name="delete" value="delete">Удалить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||
41
views/prjclean3.erb
Normal file
@@ -0,0 +1,41 @@
|
||||
<%= erb :header %>
|
||||
<script src="/js/jquery.dropdown.min.js"></script>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<h3 class="bg-secondary-subtle text-center border-bottom border-primary-subtle rounded-1 pb-1 mb-2">
|
||||
<a href="/prjedit/<%= ERB::Util.url_encode(@proj_id) %>"><%= @proj_name %></a>
|
||||
</h3>
|
||||
<div class="pb-2"><%= @proj_descr %></div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="vstack gap-3">
|
||||
<form action="/prjclean_step3/<%= ERB::Util.url_encode(@proj_id) %>" method="post">
|
||||
<div class="mb-3">
|
||||
<label for="prjrpmlst" class="form-label">Пакеты для удаления (могут быть удалены из списка)</label>
|
||||
<div class="prjrpmlist">
|
||||
<select class="form-control" id="prjrpmlst" name="prjrpmlst[]" multiple>
|
||||
<% @del_list.each do |item| %>
|
||||
<option value="<%= item %>" selected><%= File.basename(item) %></option>
|
||||
<% end %>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div style="padding-top: 45px;"></div>
|
||||
</div>
|
||||
<div class="mb-3 text-center">
|
||||
<button type="submit" class="btn btn-primary" name="cancel" value="cancel">Отменить</button>
|
||||
<button type="submit" class="btn btn-danger" name="delete" value="delete">Удалить</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$('.prjrpmlist').dropdown({
|
||||
searchNoData: '<li style="color:#ddd">Нет данных</li>',
|
||||
});
|
||||
</script>
|
||||
<%= erb :footer %>
|
||||
22
views/prjclean3_post.erb
Normal file
@@ -0,0 +1,22 @@
|
||||
<%= erb :header %>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<h3 class="bg-secondary-subtle text-center border-bottom border-primary-subtle rounded-1 pb-1 mb-2">
|
||||
<a href="/prjedit/<%= ERB::Util.url_encode(@proj_id) %>"><%= @proj_name %></a>
|
||||
</h3>
|
||||
<div class="pb-2"><%= @proj_descr %></div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="vstack gap-3">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item active" aria-current="true">Список удаленных пакетов (не забудьте пересоздать repoview)</li>
|
||||
<% @rpm_list.each do |item| %>
|
||||
<li class="list-group-item"><%= item %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||
@@ -23,6 +23,14 @@
|
||||
<% end %>
|
||||
<label class="form-check-label" for="nopublic">Не публиковать отладочные пакеты и исходные коды</label>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<% if @old_tmpbld.nil? %>
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="tmpbld" name="tmpbld" vlaue="tmpbld">
|
||||
<% else %>
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="tmpbld" name="tmpbld" vlaue="tmpbld" checked>
|
||||
<% end %>
|
||||
<label class="form-check-label" for="tmpbld">Добавлять к версии пакетов дату и ID сборки</label>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="conflist" class="form-label">Выберите конфигурацию окружения сборки для проекта</label>
|
||||
<div class="conflist">
|
||||
|
||||
@@ -33,6 +33,13 @@
|
||||
Запрещена публикация пакетов с исходными кодами
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="pb-2">
|
||||
<% if @proj_tmpbuild == 1 %>
|
||||
Сборка с добавлением даты и идентификатора
|
||||
<% else %>
|
||||
Обычная сборка пакетов
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="list-group">
|
||||
<a href="/prjcfg/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-pen"></i><span class="ms-2">Редактировать
|
||||
@@ -42,13 +49,18 @@
|
||||
<a href="/prjrpm/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-box"></i><span class="ms-2">Список всех
|
||||
пакетов</span></a>
|
||||
<a href="/prjshot/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-camera-fill"></i><span class="ms-2">Управление снимками репозитория</span></a>
|
||||
<a href="/prjbuilds/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-list"></i><span class="ms-2">Список сборок проекта</span></a>
|
||||
<a href="/prjsign/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-pencil-square"></i><span class="ms-2">Подписать</span></a>
|
||||
<a href="/prjpub/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-nut"></i><span class="ms-2">Опубликовать во
|
||||
внешний репозиторий</span></a>
|
||||
<a href="/prjsignview/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-file-earmark-diff"></i><span class="ms-2">Просмотр подписанного репозитория</span></a>
|
||||
<a href="/prjremoteaddr/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-pin-map-fill"></i><span class="ms-2">Установить адрес подписаннго репозитория</span></a>
|
||||
<a href="/prjclean/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-suitcase"></i><span class="ms-2">Удалить старые версии пакетов</span></a>
|
||||
<a href="/prjdelete/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
|
||||
class="bi bi-trash"></i><span class="ms-2">Удалить проект</span></a>
|
||||
</div>
|
||||
|
||||
42
views/prjshot1.erb
Normal file
@@ -0,0 +1,42 @@
|
||||
<%= erb :header %>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-4">
|
||||
<h3 class="bg-secondary-subtle text-center border-bottom border-primary-subtle rounded-1 pb-1 mb-2">
|
||||
<%= @proj_name %>
|
||||
</h3>
|
||||
<div class="pb-2"><%= @proj_descr %></div>
|
||||
<div>
|
||||
<table class="table">
|
||||
<tbody>
|
||||
<% @snap_list.each do |item| %>
|
||||
<tr>
|
||||
<td><a href="/prjshot/<%= ERB::Util.url_encode(@proj_id) %>?snap=<%= ERB::Util.url_encode(item) %>"><%= item %></a></td>
|
||||
<td><a href="/prjsnap_delete/<%= ERB::Util.url_encode(@proj_id) %>?snap=<%= ERB::Util.url_encode(item) %>" class="icon-link"><i
|
||||
class="bi bi-trash3"></i></a></td>
|
||||
<td><a href="/prjsnap_restore/<%= ERB::Util.url_encode(@proj_id) %>?snap=<%= ERB::Util.url_encode(item) %>" class="icon-link"><i
|
||||
class="bi bi-capslock"></i></a></td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-8">
|
||||
<div class="vstack gap-3">
|
||||
<div class="p-2">
|
||||
<a href="/prjsnap_add/<%= ERB::Util.url_encode(@proj_id) %>" class="icon-link"><i
|
||||
class="bi bi-plus-circle"></i><span class="ms-2">Создать снимок текущего репозитория</span></a>
|
||||
</div>
|
||||
<div class="p-2">
|
||||
<ul class="list-group">
|
||||
<% @rpms_list.each do |rpm| %>
|
||||
<li class="list-group-item"><%= rpm %></li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<%= erb :footer %>
|
||||