Compare commits

...

49 Commits

Author SHA1 Message Date
alexey
cbe4505d1e Fix for empty changelog 2026-03-16 20:19:59 +03:00
alexey
97b6db5b31 Fix version pasring for temporary build projects 2026-03-16 20:06:49 +03:00
alexey
46b2bc0d8e Fixed error on incorrect change log 2026-03-15 23:38:58 +03:00
Alexey Berezhok
77ee51c606 Update version 2025-12-27 16:36:04 +03:00
Alexey Berezhok
e0aedae6e0 Update documentation 2025-12-27 16:05:23 +03:00
Alexey Berezhok
4d0b237127 Added git info and docs updates 2025-12-26 23:01:25 +03:00
Alexey Berezhok
d098ffd3d0 Fixed typo in page templates 2025-12-25 23:39:27 +03:00
Alexey Berezhok
4eb58fc8ad Fixed project deletion errors 2025-12-25 23:32:53 +03:00
Alexey Berezhok
40f8a546f5 Added work with snapshots. Part 2 2025-12-25 15:10:24 +03:00
Alexey Berezhok
5dd481a81c Added snapshot. Part 2 2025-12-24 23:20:41 +03:00
Alexey Berezhok
8b4410eee7 Added snapshot 2025-12-24 23:01:50 +03:00
Alexey Berezhok
bacea404e2 Recreate repo on clean 2025-12-23 23:57:23 +03:00
Alexey Berezhok
5e59ea319f Added deleting of signed rpms on clean rpms 2025-12-23 23:45:33 +03:00
Alexey Berezhok
5f784270e2 Added deletion rpms 2025-12-23 23:33:01 +03:00
Alexey Berezhok
97ee27fe79 Fixed repoview generator 2025-12-05 00:06:26 +03:00
Alexey Berezhok
482a586b6e Added clean mock cache 2025-12-04 23:18:15 +03:00
Alexey Berezhok
f2136260c9 Added build time showing
Fixed package downloading 503 error
2025-12-03 23:47:03 +03:00
Alexey Berezhok
54bf61d301 Added some comments 2025-11-15 23:42:24 +03:00
Alexey Berezhok
89f92d265a - Добавлена поддержка ожидания сборки
- Изменен порядок сортировки новых пакетов в repoview
2025-11-15 22:54:56 +03:00
Alexey Berezhok
d50ee1a708 Fix include additional repos error 2025-11-09 22:50:21 +03:00
Alexey Berezhok
e1355d68b4 Fixed readme 2025-11-09 13:43:59 +03:00
Alexey Berezhok
7f6ca3c18a Added vesrion info 2025-11-09 13:40:37 +03:00
Alexey Berezhok
546ebf87bb Added documentation changes 2025-11-09 13:36:49 +03:00
Alexey Berezhok
5fef1797a7 Added new repoview 2025-11-09 00:23:42 +03:00
Alexey Berezhok
e354e20f2d Added projects with tmp builds 2025-11-07 23:31:30 +03:00
alexey
3bb88d104e Change license from GPL to MIT 2025-04-02 15:31:20 +03:00
alexey
ad89cc5d59 Added skip for repoview 2025-04-01 22:44:01 +03:00
alexey
281c9c2e4f Spell fixes 2025-04-01 22:11:53 +03:00
alexey
07c765637c delete tar gz 2025-03-27 23:16:59 +03:00
alexey
95772673f6 Delete docs 2025-03-27 23:15:50 +03:00
alexey
8ee140ae13 Update docs 2025-03-27 23:14:38 +03:00
alexey
74ca178a8b Added update page 2025-03-27 20:20:59 +03:00
alexey
c3d70f8dc5 Fixed repo name 2025-03-26 17:30:19 +03:00
alexey
99ec1ae40a Fixed README 2025-03-25 23:47:49 +03:00
alexey
18758407c7 Added docs 2 2025-03-25 23:40:45 +03:00
alexey
ba79ecd029 Added docs 2025-03-25 23:27:36 +03:00
alexey
5ae6f97c69 Added install script 2025-03-24 23:37:21 +03:00
alexey
10310ed874 Added pub key place 2025-03-23 21:01:31 +03:00
alexey
a33c5e8f92 Fixes 2025-03-23 20:32:54 +03:00
alexey
10255953ee Fixes 2025-03-23 20:29:43 +03:00
alexey
757a0e51a9 Fixes 2025-03-23 20:23:03 +03:00
alexey
7d03ff3bd3 Fixes 2025-03-23 20:16:05 +03:00
alexey
5fe73247b0 Fixes 2025-03-23 20:13:17 +03:00
alexey
081e9bc86d Added fixes 2025-03-23 17:27:22 +03:00
alexey
b473cf87e1 Added fixes 2025-03-22 23:05:25 +03:00
alexey
224344339e Add binding 2025-03-22 22:56:32 +03:00
alexey
b5bf43a6d5 Added service changes 2025-03-22 22:31:41 +03:00
alexey
32bd17f2d5 Sign. Part 4 2025-03-22 22:04:26 +03:00
alexey
a1fd986012 Sign. Part 3 2025-03-22 21:30:36 +03:00
101 changed files with 2356 additions and 315 deletions

14
.gitignore vendored
View File

@@ -1,5 +1,13 @@
vendor vendor
repo repo/*
db/*.sqlite3 db/*.sqlite3
projects projects/*
.ruby-lsp .ruby-lsp
keys/private
keys/public
keys/save
logs/actions.log
!.gitignore
!projects/empty
!repo/empty

View File

@@ -17,9 +17,7 @@ gem "inifile", "~> 3.0"
gem "carrierwave", "~> 3.1" gem "carrierwave", "~> 3.1"
gem "data_mapper", "~> 1.2" gem "json", "~> 2.5"
gem "json", "~> 1.8"
gem "stringio", "~> 3.1" gem "stringio", "~> 3.1"
@@ -36,3 +34,5 @@ gem "sequel", "~> 5.89"
gem "ffi", "~> 1.17" gem "ffi", "~> 1.17"
gem "ptools", "~> 1.5" gem "ptools", "~> 1.5"
gem "ostruct", "~> 0.6.1"

View File

@@ -1,16 +1,16 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
activemodel (8.0.1) activemodel (8.1.1)
activesupport (= 8.0.1) activesupport (= 8.1.1)
activesupport (8.0.1) activesupport (8.1.1)
base64 base64
benchmark (>= 0.3)
bigdecimal bigdecimal
concurrent-ruby (~> 1.0, >= 1.3.1) concurrent-ruby (~> 1.0, >= 1.3.1)
connection_pool (>= 2.2.5) connection_pool (>= 2.2.5)
drb drb
i18n (>= 1.6, < 2) i18n (>= 1.6, < 2)
json
logger (>= 1.4.2) logger (>= 1.4.2)
minitest (>= 5.1) minitest (>= 5.1)
securerandom (>= 0.3) securerandom (>= 0.3)
@@ -18,13 +18,9 @@ GEM
uri (>= 0.13.1) uri (>= 0.13.1)
addressable (2.8.7) addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0) public_suffix (>= 2.0.2, < 7.0)
base64 (0.2.0) base64 (0.3.0)
bcrypt (3.1.20) bigdecimal (3.3.1)
bcrypt-ruby (3.1.5) carrierwave (3.1.2)
bcrypt (>= 3.1.3)
benchmark (0.4.0)
bigdecimal (3.1.9)
carrierwave (3.1.1)
activemodel (>= 6.0.0) activemodel (>= 6.0.0)
activesupport (>= 6.0.0) activesupport (>= 6.0.0)
addressable (~> 2.6) addressable (~> 2.6)
@@ -32,129 +28,82 @@ GEM
marcel (~> 1.0.0) marcel (~> 1.0.0)
ssrf_filter (~> 1.0) ssrf_filter (~> 1.0)
concurrent-ruby (1.3.5) concurrent-ruby (1.3.5)
connection_pool (2.5.0) connection_pool (2.5.4)
data_mapper (1.2.0) drb (2.2.3)
dm-aggregates (~> 1.2.0) ffi (1.17.2-aarch64-linux-gnu)
dm-constraints (~> 1.2.0) ffi (1.17.2-aarch64-linux-musl)
dm-core (~> 1.2.0) ffi (1.17.2-arm-linux-gnu)
dm-migrations (~> 1.2.0) ffi (1.17.2-arm-linux-musl)
dm-serializer (~> 1.2.0) ffi (1.17.2-arm64-darwin)
dm-timestamps (~> 1.2.0) ffi (1.17.2-x86-linux-gnu)
dm-transactions (~> 1.2.0) ffi (1.17.2-x86-linux-musl)
dm-types (~> 1.2.0) ffi (1.17.2-x86_64-darwin)
dm-validations (~> 1.2.0) ffi (1.17.2-x86_64-linux-gnu)
dm-aggregates (1.2.0) ffi (1.17.2-x86_64-linux-musl)
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)
i18n (1.14.7) i18n (1.14.7)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
image_processing (1.14.0) image_processing (1.14.0)
mini_magick (>= 4.9.5, < 6) mini_magick (>= 4.9.5, < 6)
ruby-vips (>= 2.0.17, < 3) ruby-vips (>= 2.0.17, < 3)
inifile (3.0.0) inifile (3.0.0)
json (1.8.6) json (2.15.2)
json_pure (1.8.6) logger (1.7.0)
logger (1.6.6)
marcel (1.0.4) marcel (1.0.4)
mini_magick (5.1.2) mini_magick (5.3.1)
benchmark
logger logger
mini_portile2 (2.8.8) minitest (5.26.0)
minitest (5.25.4) mustermann (3.0.4)
multi_json (1.15.0)
mustermann (3.0.3)
ruby2_keywords (~> 0.0.1) ruby2_keywords (~> 0.0.1)
nio4r (2.7.4) nio4r (2.7.5)
ostruct (0.6.3)
ptools (1.5.0) ptools (1.5.0)
public_suffix (6.0.1) public_suffix (6.0.2)
puma (6.6.0) puma (6.6.1)
nio4r (~> 2.0) nio4r (~> 2.0)
rack (3.1.10) rack (3.2.4)
rack-protection (4.1.1) rack-protection (4.2.1)
base64 (>= 0.1.0) base64 (>= 0.1.0)
logger (>= 1.6.0) logger (>= 1.6.0)
rack (>= 3.0.0, < 4) rack (>= 3.0.0, < 4)
rack-session (2.1.0) rack-session (2.1.1)
base64 (>= 0.1.0) base64 (>= 0.1.0)
rack (>= 3.0.0) rack (>= 3.0.0)
rackup (2.2.1) rackup (2.2.1)
rack (>= 3) rack (>= 3)
ruby-vips (2.2.3) ruby-vips (2.2.5)
ffi (~> 1.12) ffi (~> 1.12)
logger logger
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rugged (1.9.0) rugged (1.9.0)
securerandom (0.4.1) securerandom (0.4.1)
sequel (5.89.0) sequel (5.98.0)
bigdecimal bigdecimal
shotgun (0.9.2) shotgun (0.9.2)
rack (>= 1.0) rack (>= 1.0)
sinatra (4.1.1) sinatra (4.2.1)
logger (>= 1.6.0) logger (>= 1.6.0)
mustermann (~> 3.0) mustermann (~> 3.0)
rack (>= 3.0.0, < 4) rack (>= 3.0.0, < 4)
rack-protection (= 4.1.1) rack-protection (= 4.2.1)
rack-session (>= 2.0.0, < 3) rack-session (>= 2.0.0, < 3)
tilt (~> 2.0) tilt (~> 2.0)
sqlite (1.0.2) sqlite (1.0.2)
sqlite3 (2.5.0) sqlite3 (2.7.4-aarch64-linux-gnu)
mini_portile2 (~> 2.8.0) sqlite3 (2.7.4-aarch64-linux-musl)
sqlite3 (2.5.0-aarch64-linux-gnu) sqlite3 (2.7.4-arm-linux-gnu)
sqlite3 (2.5.0-aarch64-linux-musl) sqlite3 (2.7.4-arm-linux-musl)
sqlite3 (2.5.0-arm-linux-gnu) sqlite3 (2.7.4-arm64-darwin)
sqlite3 (2.5.0-arm-linux-musl) sqlite3 (2.7.4-x86-linux-gnu)
sqlite3 (2.5.0-arm64-darwin) sqlite3 (2.7.4-x86-linux-musl)
sqlite3 (2.5.0-x86-linux-gnu) sqlite3 (2.7.4-x86_64-darwin)
sqlite3 (2.5.0-x86-linux-musl) sqlite3 (2.7.4-x86_64-linux-gnu)
sqlite3 (2.5.0-x86_64-darwin) sqlite3 (2.7.4-x86_64-linux-musl)
sqlite3 (2.5.0-x86_64-linux-gnu) ssrf_filter (1.3.0)
sqlite3 (2.5.0-x86_64-linux-musl) stringio (3.1.7)
ssrf_filter (1.2.0) tilt (2.6.1)
stringex (1.5.1)
stringio (3.1.3)
tilt (2.6.0)
tzinfo (2.0.6) tzinfo (2.0.6)
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
uri (1.0.2) uri (1.1.1)
uuidtools (2.2.0)
PLATFORMS PLATFORMS
aarch64-linux-gnu aarch64-linux-gnu
@@ -162,7 +111,6 @@ PLATFORMS
arm-linux-gnu arm-linux-gnu
arm-linux-musl arm-linux-musl
arm64-darwin arm64-darwin
ruby
x86-linux-gnu x86-linux-gnu
x86-linux-musl x86-linux-musl
x86_64-darwin x86_64-darwin
@@ -171,10 +119,10 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
carrierwave (~> 3.1) carrierwave (~> 3.1)
data_mapper (~> 1.2)
ffi (~> 1.17) ffi (~> 1.17)
inifile (~> 3.0) inifile (~> 3.0)
json (~> 1.8) json (~> 2.5)
ostruct (~> 0.6.1)
ptools (~> 1.5) ptools (~> 1.5)
puma (~> 6.6) puma (~> 6.6)
rackup (~> 2.2) rackup (~> 2.2)
@@ -187,4 +135,4 @@ DEPENDENCIES
stringio (~> 3.1) stringio (~> 3.1)
BUNDLED WITH BUNDLED WITH
2.6.3 2.7.2

129
LICENSE
View File

@@ -1,117 +1,28 @@
GNU GENERAL PUBLIC LICENSE The MIT License (MIT)
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc. Copyright (c) 2025 Alexey Berezhok
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
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
View File

@@ -1,11 +1,155 @@
## Запуск # MockGUI
Обертка над mock для организации графического интерфейса сборки RPM пакетов
![Главный экран MockGUI](docs/mock-gui/docs/img/mockgui_mainscreen.png)
## Где можно запустить
ПО может быть установлено на следующих ОС:
* 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
View 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 пакетов

496
app.rb
View File

@@ -19,6 +19,16 @@ require_relative "classes/utilities"
require_relative "classes/projects" require_relative "classes/projects"
require_relative "classes/configs" 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) def print_error_page(error_status, error_meaasge)
@page_name = "Ошибка выполнения" @page_name = "Ошибка выполнения"
@status_err = error_status @status_err = error_status
@@ -28,7 +38,9 @@ end
cfg = IniConfig.new() cfg = IniConfig.new()
db = DBase.new(cfg) db = DBase.new(cfg)
db.cancel_hang_builds
set :bind, "0.0.0.0"
set :port, cfg.get_port set :port, cfg.get_port
set :public_folder, File.dirname(__FILE__) + "/public" set :public_folder, File.dirname(__FILE__) + "/public"
use Rack::Session::Pool, :expire_after => 2592000 use Rack::Session::Pool, :expire_after => 2592000
@@ -332,6 +344,7 @@ get "/prjcreate" do
@local_list = result[:local] @local_list = result[:local]
@old_nopublic = session[:prj_old_nopublic] @old_nopublic = session[:prj_old_nopublic]
@error_data = session[:prjcreate_error] @error_data = session[:prjcreate_error]
@old_tmpbld = session[:prj_old_tmpbld]
session[:prjcreate_error] = nil session[:prjcreate_error] = nil
erb :prjcrt erb :prjcrt
end end
@@ -341,12 +354,13 @@ post "/prjcreate" do
session[:prj_old_description] = params["description"] session[:prj_old_description] = params["description"]
session[:prj_old_list] = params["conflist"] session[:prj_old_list] = params["conflist"]
session[:prj_old_nopublic] = params["nopublic"] 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 == "" if params["projname"].nil? || params["description"].nil? || params["projname"].strip == "" || params["description"].strip == "" || params["conflist"].nil? || params["conflist"].strip == ""
session[:rcpcreate_error] = "Имя проекта, описание и окружение сборки не должны быть пустыми" session[:rcpcreate_error] = "Имя проекта, описание и окружение сборки не должны быть пустыми"
redirect "/prjcreate" redirect "/prjcreate"
else else
prj = ProjectsActions.new(cfg.get_projects_path, db) 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 if result != 0
session[:prjcreate_error] = prj.error session[:prjcreate_error] = prj.error
redirect "/prjcreate" redirect "/prjcreate"
@@ -355,6 +369,7 @@ post "/prjcreate" do
session[:prj_old_description] = nil session[:prj_old_description] = nil
session[:prj_old_list] = nil session[:prj_old_list] = nil
session[:prj_old_nopublic] = nil session[:prj_old_nopublic] = nil
session[:prj_old_tmpbld] = nil
redirect "/projs" redirect "/projs"
end end
end end
@@ -384,6 +399,7 @@ get "/prjedit/:id" do
@proj_descr = prj_info[:descr] @proj_descr = prj_info[:descr]
@proj_id = prj_info[:id] @proj_id = prj_info[:id]
@proj_public = prj_info[:public] @proj_public = prj_info[:public]
@proj_tmpbuild = prj_info[:tmpstpbuild]
repo_lst = repo.getrepos repo_lst = repo.getrepos
proj_repo_list = prj.get_project_gits(prj_info[:id], repo) proj_repo_list = prj.get_project_gits(prj_info[:id], repo)
@repo_list = repo_lst.reject do |item| @repo_list = repo_lst.reject do |item|
@@ -403,6 +419,177 @@ get "/prjedit/:id" do
end end
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 post "/prjagit/:id" do
prj = ProjectsActions.new(cfg.get_projects_path, db) prj = ProjectsActions.new(cfg.get_projects_path, db)
if prj.path.nil? if prj.path.nil?
@@ -597,7 +784,6 @@ post "/prjcfg/:id" do
if prj_info.nil? if prj_info.nil?
print_error_page(503, "Путь к проектам не существует") print_error_page(503, "Путь к проектам не существует")
else else
pp params
unless params["cancel"].nil? unless params["cancel"].nil?
redirect "/prjedit/#{params["id"]}" redirect "/prjedit/#{params["id"]}"
else else
@@ -765,7 +951,7 @@ get "/gitbld/:id/:git_id" do
@git_name = git_info[:reponame] @git_name = git_info[:reponame]
@proj_id = params["id"] @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 if @build_id == 0
print_error_page(503, "Ошибка создания или получения информации о сборке, возможно проблемы с файлом блокировки") print_error_page(503, "Ошибка создания или получения информации о сборке, возможно проблемы с файлом блокировки")
@@ -948,12 +1134,25 @@ get "/buildinfof/:build_id" do
end end
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 get "/buildinfofraw" do
if params["file"].nil? || !File.exist?(params["file"]) if params["file"].nil? || !File.exist?(params["file"])
print_error_page(503, "Файл не найден") print_error_page(503, "Файл не найден")
else else
if !File.binary?(params["file"]) && params["file"].start_with?(File.join(Dir.pwd(), cfg.get_projects_path)) 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 else
print_error_page(503, "Файл не может быть скачан") print_error_page(503, "Файл не может быть скачан")
end end
@@ -1032,10 +1231,15 @@ get "/prjrpm/:id" do
@file_content << "Версия пакета: #{rpm_info.version}" @file_content << "Версия пакета: #{rpm_info.version}"
@file_content << "" @file_content << ""
@file_content << "Changelog:" @file_content << "Changelog:"
rpm_info.changelog.first(10).each do |entry| begin
@file_content << "#{entry.time} #{entry.name}" rpm_info.changelog.first(10).each do |entry|
@file_content << "#{entry.text}" @file_content << "#{entry.time} #{entry.name}"
@file_content << "---------------" @file_content << "#{entry.text}"
@file_content << "---------------"
end
rescue
# Если есть ошибка с undefined local variable or method, пропускаем changelog
@file_content << "Changelog недоступен"
end end
@file_content << "---------------" @file_content << "---------------"
@file_content << "Файлы:" @file_content << "Файлы:"
@@ -1271,7 +1475,7 @@ post "/prjsign/:id" do
print_error_page(503, "Путь к проектам не существует") print_error_page(503, "Путь к проектам не существует")
else else
if params["cancel"].nil? && params["sign"] == "sign" 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) err = prj.sign_project(params["id"], cfg.get_keys_path, params["password"], "/prjsignview/#{prj_info[:id]}", cfg.get_repoview_path)
unless err.nil? unless err.nil?
session[:prj_modal_info] = "Ошибка добавления подписи пакетов проекта" session[:prj_modal_info] = "Ошибка добавления подписи пакетов проекта"
session[:prj_modal_text] = err session[:prj_modal_text] = err
@@ -1295,7 +1499,7 @@ get "/prjremoteaddr/:id" do
@proj_name = prj_info[:projname] @proj_name = prj_info[:projname]
@proj_id = params["id"] @proj_id = params["id"]
@addres = prj_info[:remote_address] @addres = prj_info[:remote_address]
erb :prjremote erb :projremote
end end
end end
end end
@@ -1339,8 +1543,7 @@ get "/prjsignview/:id" do
else else
gpgKeys = RepoManagerKeys.new(cfg.get_keys_path) gpgKeys = RepoManagerKeys.new(cfg.get_keys_path)
if gpgKeys.check_key_exists if gpgKeys.check_key_exists
sign_path = prj.get_sign_path(params["id"]) redirect "/prjsignview/#{params["id"]}/"
send_file File.join(sign_path, "index.html")
else else
@page_name = "Ошибка подписания проекта #{prj_info[:projname]}" @page_name = "Ошибка подписания проекта #{prj_info[:projname]}"
erb :gpgerror erb :gpgerror
@@ -1350,7 +1553,274 @@ get "/prjsignview/:id" do
end end
get "/prjsignview/:id/*" do get "/prjsignview/:id/*" do
"test" 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 end
not_found do not_found do

View File

@@ -1,20 +1,24 @@
require "inifile" require "inifile"
# Класс для работы с конфигурацией INI-файла
class IniConfig class IniConfig
attr :path attr :path
# Конструктор класса, принимает путь к INI-файлу и парсит его содержимое
def initialize(in_path = "config.ini") def initialize(in_path = "config.ini")
@path = in_path @path = in_path
@config = {} @config = {}
parse_ini parse_ini
end end
# Метод для парсинга INI-файла и загрузки его содержимого в хэш
def parse_ini() def parse_ini()
return if path.nil? return if path.nil?
return unless File.exist? path return unless File.exist? path
@config = IniFile.load(path) @config = IniFile.load(path)
end end
# Метод для получения порта сервера из конфигурации, если он задан, иначе возвращает значение по умолчанию (8080)
def get_port() def get_port()
unless @config["server"]["port"].nil? unless @config["server"]["port"].nil?
@config["server"]["port"].to_i @config["server"]["port"].to_i
@@ -23,6 +27,7 @@ class IniConfig
end end
end end
# Метод для получения репозитория из конфигурации, если он задан, иначе возвращает значение по умолчанию ("repo")
def get_repo() def get_repo()
unless @config["repo"]["repo"].nil? unless @config["repo"]["repo"].nil?
@config["repo"]["repo"].to_s @config["repo"]["repo"].to_s
@@ -31,6 +36,7 @@ class IniConfig
end end
end end
# Метод для получения базы данных из конфигурации, если она задана, иначе возвращает значение по умолчанию ("db")
def get_db() def get_db()
unless @config["server"]["db"].nil? unless @config["server"]["db"].nil?
@config["server"]["db"].to_s @config["server"]["db"].to_s
@@ -39,6 +45,7 @@ class IniConfig
end end
end end
# Метод для получения скрытых конфигураций из конфигурации, если они заданы, иначе возвращает пустой массив
def get_configs_hide() def get_configs_hide()
unless @config["configs"]["hide"].nil? unless @config["configs"]["hide"].nil?
@config["configs"]["hide"].split(",").map { |item| item.strip } @config["configs"]["hide"].split(",").map { |item| item.strip }
@@ -47,6 +54,7 @@ class IniConfig
end end
end end
# Метод для получения выбранных конфигураций из конфигурации, если они заданы, иначе возвращает пустой массив
def get_configs_selected() def get_configs_selected()
unless @config["configs"]["selected"].nil? unless @config["configs"]["selected"].nil?
@config["configs"]["selected"].split(",").map { |item| item.strip } @config["configs"]["selected"].split(",").map { |item| item.strip }
@@ -55,6 +63,7 @@ class IniConfig
end end
end end
# Метод для получения пути к проектам из конфигурации, если он задан, иначе возвращает значение по умолчанию ("projects")
def get_projects_path() def get_projects_path()
unless @config["projects"]["path"].nil? unless @config["projects"]["path"].nil?
@config["projects"]["path"].to_s @config["projects"]["path"].to_s
@@ -63,6 +72,7 @@ class IniConfig
end end
end end
# Метод для получения пути к счетчику из конфигурации, если он задан, иначе возвращает значение по умолчанию ("locks/counter")
def get_counter_path() def get_counter_path()
unless @config["counter"]["path"].nil? unless @config["counter"]["path"].nil?
@config["counter"]["path"].to_s @config["counter"]["path"].to_s
@@ -71,6 +81,16 @@ class IniConfig
end end
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() def get_items_per_page()
unless @config["pages"]["items_per_page"].nil? unless @config["pages"]["items_per_page"].nil?
res = @config["pages"]["items_per_page"].to_i res = @config["pages"]["items_per_page"].to_i
@@ -83,6 +103,7 @@ class IniConfig
end end
end end
# Метод для получения пути к ключам из конфигурации, если он задан, иначе возвращает значение по умолчанию ("keys")
def get_keys_path() def get_keys_path()
unless @config["sign"]["path"].nil? unless @config["sign"]["path"].nil?
@config["sign"]["path"].to_s @config["sign"]["path"].to_s
@@ -91,6 +112,7 @@ class IniConfig
end end
end end
# Метод для получения пути к реповьюверу из конфигурации, если он задан, иначе возвращает значение по умолчанию ("repoview")
def get_repoview_path() def get_repoview_path()
unless @config["repoview"]["path"].nil? unless @config["repoview"]["path"].nil?
@config["repoview"]["path"].to_s @config["repoview"]["path"].to_s

View File

@@ -1,27 +1,49 @@
# Подключаем файл utilities.rb для использования его методов
require_relative "utilities" require_relative "utilities"
# Класс ConfigsList для работы со списком конфигураций
class ConfigsList class ConfigsList
# Определение свойств класса
attr :error, :cfg attr :error, :cfg
# Конструктор класса с параметром cfg
def initialize(cfg) def initialize(cfg)
@cfg = cfg @cfg = cfg
end end
# Метод get_configs для получения списка конфигураций
def get_configs def get_configs
# Получаем список скрытых конфигураций из cfg
hide_list = @cfg.get_configs_hide hide_list = @cfg.get_configs_hide
# Получаем список выбранных конфигураций из cfg
select_list = @cfg.get_configs_selected 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) } 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? if list_global.nil?
list_global = [] list_global = []
end end
# Создаем список локальных конфигураций в директории ~/.config/mock/*.cfg
list_local = Dir["~/.config/mock/*.cfg"].map { |item| [File.dirname(item), File.basename(item, ".cfg"), item] } list_local = Dir["~/.config/mock/*.cfg"].map { |item| [File.dirname(item), File.basename(item, ".cfg"), item] }
# Проверяем наличие локальных конфигураций и создаем пустой список, если их нет
if list_local.nil? if list_local.nil?
list_local = [] list_local = []
end end
# Создаем список выбранных конфигураций из глобального и локального списка
list_selected = (list_global + list_local).select { |item| check_partname_in_array(item[1], select_list) } list_selected = (list_global + list_local).select { |item| check_partname_in_array(item[1], select_list) }
# Проверяем наличие выбранных конфигураций и создаем пустой список, если их нет
if list_selected.nil? if list_selected.nil?
list_selected = [] list_selected = []
end end
# Возвращаем хеш с глобальными, локальными и выбранными конфигурациями
{ :global => list_global, :local => list_local, :selected => list_selected } { :global => list_global, :local => list_local, :selected => list_selected }
end end
end end

View File

@@ -72,6 +72,7 @@ class DBase
id = rep_id[:id] id = rep_id[:id]
RepocRecips.where(repo_id: id).delete RepocRecips.where(repo_id: id).delete
ReposProjects.where(repo_id: id).delete ReposProjects.where(repo_id: id).delete
Repos.where(reponame: repo_name).delete
end end
end end
@@ -145,7 +146,7 @@ class DBase
Projects[id] Projects[id]
end end
def proj_create(proj_name, proj_descr, nopublic) def proj_create(proj_name, proj_descr, nopublic, tmpbld)
@error = nil @error = nil
data = Projects.where(projname: proj_name).first data = Projects.where(projname: proj_name).first
if data.nil? if data.nil?
@@ -153,7 +154,11 @@ class DBase
unless nopublic.nil? unless nopublic.nil?
public_proj = 0 public_proj = 0
end 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 @last_id = id
else else
@error = "Данный проект уже существует" @error = "Данный проект уже существует"
@@ -244,6 +249,10 @@ class DBase
BuildTask.where(id: build_id.to_i).update(result: status.to_i) BuildTask.where(id: build_id.to_i).update(result: status.to_i)
end 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) def update_build_task_error_log(build_id, path)
BuildTask.where(id: build_id.to_i).update(errlogpath: path) BuildTask.where(id: build_id.to_i).update(errlogpath: path)
end end
@@ -311,7 +320,7 @@ class DBase
end end
def get_builds() 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 end
def get_build_info(build_id) def get_build_info(build_id)
@@ -332,11 +341,11 @@ class DBase
end end
def get_builds_for_project(prj_id) 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 end
def get_builds_for_project_git(prj_id, git_id) 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 end
def delete_git_from_project(prj_id, git_id) def delete_git_from_project(prj_id, git_id)
@@ -345,15 +354,27 @@ class DBase
end end
def delete_project(prj_id) 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 ReposProjects.where(proj_id: prj_id.to_i).delete
ProjectsReposSpec.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 = BuildTask.where(proj_id: prj_id.to_i)
builds.each do |item| builds.each do |item|
rpms = BuildRpms.where(build_id: item[:id]) 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 end
BuildTask.where(proj_id: prj_id.to_i).delete 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 Projects.where(id: prj_id.to_i).delete
0
end end
def projects_with_current_as_link(prj_id) def projects_with_current_as_link(prj_id)
@@ -369,6 +390,26 @@ class DBase
end end
def set_project_address(prj_id, address) def set_project_address(prj_id, address)
ProjectsProjects.where(proj_id: prj_id.to_i).update(remote_address: 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
end end

View File

@@ -72,6 +72,24 @@ class GitRepo
repos_data repos_data
end 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) def repo_info(reponame, branch = nil)
info = {} info = {}
result = "" result = ""
@@ -97,6 +115,7 @@ class GitRepo
info[:commits] = [] info[:commits] = []
info[:branches] = [] info[:branches] = []
info[:tags] = [] info[:tags] = []
info[:files] = []
else else
ref = repo.head ref = repo.head
unless branch.nil? unless branch.nil?
@@ -104,6 +123,7 @@ class GitRepo
ref = repo.references[ref_name] ref = repo.references[ref_name]
end end
commits = [] commits = []
files = []
unless ref.nil? unless ref.nil?
walker = Rugged::Walker.new(repo) walker = Rugged::Walker.new(repo)
walker.sorting(Rugged::SORT_DATE) walker.sorting(Rugged::SORT_DATE)
@@ -111,10 +131,15 @@ class GitRepo
commits = walker.map do |commit| commits = walker.map do |commit|
{ :message => commit.message, :author => commit.author, :time => commit.time, :sha => commit.oid } { :message => commit.message, :author => commit.author, :time => commit.time, :sha => commit.oid }
end.first(10) end.first(10)
unless ref.target.nil? && ref.target.tree.nil?
files = get_git_tree(repo, ref, files)
end
end end
info[:commits] = commits info[:commits] = commits
info[:branches] = repo.branches.each_name(:local).sort info[:branches] = repo.branches.each_name(:local).sort
info[:tags] = repo.tags.map { |tag| tag.name } info[:tags] = repo.tags.map { |tag| tag.name }
info[:files] = files
end end
else else
@error = result @error = result

View File

@@ -4,6 +4,7 @@ require "fileutils"
require "logger" require "logger"
require_relative "repomanage" require_relative "repomanage"
require "digest" require "digest"
require "date"
BUILD_STRUCTURE = { BUILD_STRUCTURE = {
:SRC => "src", :SRC => "src",
@@ -13,9 +14,9 @@ BUILD_STRUCTURE = {
} }
class MockManager 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 @error = nil
unless File.exist? (path) unless File.exist? (path)
Dir.mkdir(path) Dir.mkdir(path)
@@ -32,6 +33,7 @@ class MockManager
@spec = spec_file @spec = spec_file
@repo_lock = repo_lock @repo_lock = repo_lock
@git_id = git_id @git_id = git_id
@tmp_bld = tmp_bld
File.open(cfg_counter_path, "r+") do |f| File.open(cfg_counter_path, "r+") do |f|
f.flock(File::LOCK_EX) f.flock(File::LOCK_EX)
@@ -110,6 +112,39 @@ class MockManager
@log.info("Подготовка SRCRPM") @log.info("Подготовка SRCRPM")
spec_file = File.join(@tmp_src, @spec) spec_file = File.join(@tmp_src, @spec)
if File.exist?(spec_file) 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 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) 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) @log.debug(cmd_args)
@@ -240,7 +275,7 @@ class MockManager
end end
end end
def build_task() def build_task(build_lock)
@error = false @error = false
@db.before_fork @db.before_fork
spock = Spork.spork(:logger => log) do spock = Spork.spork(:logger => log) do
@@ -248,33 +283,56 @@ class MockManager
old_stdout = $stdout.dup old_stdout = $stdout.dup
$stdout = File.open(@process_log, "w") $stdout = File.open(@process_log, "w")
@log = Logger.new($stdout) @log = Logger.new($stdout)
if @spec == ""
@error = true File.open(build_lock,"wb") do |global_lock|
@log.error("Не могу найти spec файл") global_lock.flock(File::LOCK_EX)
end global_lock.rewind
begin build_info = @db.get_build_task_status(build_id)
prepare_structure if @error == false unless build_info.nil?
prepare_src if @error == false if build_info[:result].to_i == 4
prepare_source if @error == false return
prepare_src_rpm if @error == false end
build_rpm if @error == false end
save_logs @db.update_build_task_status(@build_id, 0)
save_rpms if @error == false @db.update_build_task_begin_time(@build_id)
rescue => e
@error = true if @spec == ""
puts e @error = true
end @log.error("Не могу найти spec файл")
$stdout = old_stdout end
@log.close begin
save_prg_log prepare_structure if @error == false
clean_build prepare_src if @error == false
if @error prepare_source if @error == false
@db.update_build_task_status(@build_id, 1) prepare_src_rpm if @error == false
else build_rpm if @error == false
@db.update_build_task_status(@build_id, 2) 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
end end
@db.after_fork @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
end end

View File

@@ -12,6 +12,7 @@ PROJECTS_STRUCTURE = {
:SRCPRP => "srcprp", :SRCPRP => "srcprp",
:SIGNED => "signed", :SIGNED => "signed",
:SRC => "src", :SRC => "src",
:SNAP => "snapshot",
} }
class ProjectsActions class ProjectsActions
@@ -71,6 +72,11 @@ class ProjectsActions
File.join(proj_path, PROJECTS_STRUCTURE[:REPO]) File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
end 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) def get_project_path_git(id, gitname)
proj_path = get_project_path(id) proj_path = get_project_path(id)
File.join(proj_path, PROJECTS_STRUCTURE[:SRC], gitname) File.join(proj_path, PROJECTS_STRUCTURE[:SRC], gitname)
@@ -99,11 +105,13 @@ class ProjectsActions
internal_repo_path = File.join(internal_path, PROJECTS_STRUCTURE[:REPO]) internal_repo_path = File.join(internal_path, PROJECTS_STRUCTURE[:REPO])
internal_proj_info = internal_repo.get_project(item[:proj_id_repository]) internal_proj_info = internal_repo.get_project(item[:proj_id_repository])
proj_repo = <<~PRJ_CFG proj_repo = <<~PRJ_CFG
config_opts['dnf.conf'] += """
[#{internal_proj_info[:projname]}-repository] [#{internal_proj_info[:projname]}-repository]
name=Project repository #{internal_proj_info[:projname]} name=Project repository #{internal_proj_info[:projname]}
baseurl=file://#{internal_repo_path} baseurl=file://#{internal_repo_path}
enabled=1 enabled=1
skip_if_unavailable=True skip_if_unavailable=True
"""
PRJ_CFG PRJ_CFG
linked_prj << proj_repo linked_prj << proj_repo
end end
@@ -145,7 +153,7 @@ class ProjectsActions
generate_linked_repos(id, proj_path, proj_name, prj_incl_path) generate_linked_repos(id, proj_path, proj_name, prj_incl_path)
end end
def create_project(name, description, configuration, nopublic) def create_project(name, description, configuration, nopublic, tmpbld)
@error = nil @error = nil
ret_val = 0 ret_val = 0
project_name = sanitize_rcptname(name) project_name = sanitize_rcptname(name)
@@ -163,7 +171,7 @@ class ProjectsActions
end end
if File.exist?(configuration) if File.exist?(configuration)
generate_config(nil, configuration, fname, project_name) 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? if @error.nil?
created = true created = true
end end
@@ -267,7 +275,7 @@ class ProjectsActions
spec_file spec_file
end 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 bld_id = 0
build_ok = true build_ok = true
proj_path = get_project_path(prj_id) proj_path = get_project_path(prj_id)
@@ -281,20 +289,16 @@ class ProjectsActions
File.open(lockf_path, File::RDWR | File::CREAT) do |f| File.open(lockf_path, File::RDWR | File::CREAT) do |f|
result = f.flock(File::LOCK_EX | File::LOCK_NB) result = f.flock(File::LOCK_EX | File::LOCK_NB)
if result == false if result == false
#Файл заблокирован считать id и вывести сведения о сборке # Файл заблокирован считать id и вывести сведения о сборке
build_ok = false build_ok = false
build_id = f.gets build_id = f.gets
unless build_id.nil? build_id = build_id.strip.to_i unless build_id.nil?
build_id = build_id.strip.to_i if build_id.positive?
end
if build_id > 0
build_info = @db.get_build_task_process_log(build_id) build_info = @db.get_build_task_process_log(build_id)
unless build_info.nil? bld_id = build_info[:id] unless build_info.nil?
bld_id = build_info[:id]
end
end end
else else
#Сборка завершилась, но каталог не подчистился # Сборка завершилась, но каталог не подчистился
FileUtils.rm_rf(prepare_path) FileUtils.rm_rf(prepare_path)
f.flock(File::LOCK_UN) f.flock(File::LOCK_UN)
build_ok = true build_ok = true
@@ -304,23 +308,27 @@ class ProjectsActions
#Верная ситуация #Верная ситуация
if build_ok if build_ok
build_path = File.join(proj_path, PROJECTS_STRUCTURE[:LOGS], git_name[:reponame])
Dir.mkdir(prepare_path) Dir.mkdir(prepare_path)
lockf_path = File.join(prepare_path, "lock") lockf_path = File.join(prepare_path, "lock")
File.open(lockf_path, File::RDWR | File::CREAT) do |f| File.open(lockf_path, File::RDWR | File::CREAT) do |f|
f.flock(File::LOCK_EX) f.flock(File::LOCK_EX)
f.rewind 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]) repo_path = File.join(proj_path, PROJECTS_STRUCTURE[:REPO])
git_source = File.join(proj_path, PROJECTS_STRUCTURE[:SRC], git_name[:reponame]) 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.puts(build_id)
f.flush 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 bld_id = build_id
@db.update_build_task_error_log(build_id, mock.get_build_process_log) @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
end end
bld_id bld_id
@@ -365,7 +373,9 @@ class ProjectsActions
if linked.nil? || linked.length == 0 if linked.nil? || linked.length == 0
proj_path = get_project_path(prj_id) proj_path = get_project_path(prj_id)
FileUtils.rm_rf(proj_path, secure: true) FileUtils.rm_rf(proj_path, secure: true)
@db.delete_project(prj_id) if @db.delete_project(prj_id) != 0
@error = "На текущий проект ссылаются другие проекты. Удаление запрещено"
end
else else
@error = "На текущий проект ссылаются другие проекты. Удаление запрещено" @error = "На текущий проект ссылаются другие проекты. Удаление запрещено"
end end
@@ -403,6 +413,12 @@ class ProjectsActions
end end
end end
rpm_signed_list = get_rpms_list(sign_repo_path) 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| rpm_list = rpm_list.select do |item|
sign_repo_path_rpm = File.join(sign_repo_path, item) sign_repo_path_rpm = File.join(sign_repo_path, item)
unless File.exist?(sign_repo_path_rpm) unless File.exist?(sign_repo_path_rpm)
@@ -430,6 +446,11 @@ class ProjectsActions
end end
repo_sign.repoview(repo_url, prj[:projname], tpl_dir) repo_sign.repoview(repo_url, prj[:projname], tpl_dir)
repo_sign.create_repo 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
end end
else else
@@ -454,4 +475,67 @@ class ProjectsActions
path = get_project_path(id) path = get_project_path(id)
File.join(path, PROJECTS_STRUCTURE[:SIGNED]) File.join(path, PROJECTS_STRUCTURE[:SIGNED])
end 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 end

View File

@@ -87,37 +87,76 @@ class RepoManager
def repoview(url, repo_name, template_dir) def repoview(url, repo_name, template_dir)
rpm_list = get_rpms_list(@path) rpm_list = get_rpms_list(@path)
result = {} result = {}
nresult = {}
rpm_list.each do |item| rpm_list.each do |item|
full_rpm_path = File.join(@path, item) full_rpm_path = File.join(@path, item)
info = @reader.get_rpm_info(full_rpm_path) info = @reader.get_rpm_info(full_rpm_path)
dirName = File.dirname(item) dirName = File.dirname(item)
fileName = File.basename(item) fileName = File.basename(item)
if result[dirName].nil? if result[dirName].nil?
result[dirName] = [] result[dirName] = {}
end end
nresult[dirName] = [] if nresult[dirName].nil?
pkg_info = {} pkg_info = {}
pkg_info[:fname] = fileName pkg_info[:fname] = fileName
pkg_info[:aname] = item
pkg_info[:stat] = File.stat(full_rpm_path).ctime pkg_info[:stat] = File.stat(full_rpm_path).ctime
pkg_info[:pname] = "noname"
if info[:error].nil? if info[:error].nil?
pkg_info[:chlog] = info[:pkginfo].changelog.first(5) pkg_info[:chlog] = []
begin
pkg_info[:chlog] << info[:pkginfo].changelog.first
rescue
end
pkg_info[:pname] = info[:pkginfo].name
else else
pkg_info[:chlog] = [] pkg_info[:chlog] = []
end end
result[dirName] << pkg_info result[dirName][pkg_info[:pname]] = [] unless result[dirName].key?(pkg_info[:pname])
result[dirName][pkg_info[:pname]] << pkg_info
nresult[dirName] << pkg_info
end end
repo_name = repo_name repo_name = repo_name
repo_url = url repo_url = url
pkg_num = rpm_list.length pkg_num = rpm_list.length
repo_data = [] repo_data = []
data_keys = [] data_keys = []
lresult = {}
result.each_pair do |key, value| result.each_pair do |key, value|
result[key.to_s].sort_by! { |item| item[:fname] } 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 data_keys << key.to_s
end end
data_keys.sort! data_keys.sort!
data_keys.each do |item| data_keys.each do |item|
repo_data << result[item] repo_data << [lresult[item], item]
end 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") tpl_file = File.join(template_dir, "template.erb")
template = File.read(tpl_file) template = File.read(tpl_file)
renderer = ERB.new(template) renderer = ERB.new(template)

View File

@@ -54,3 +54,11 @@ end
def get_rpms_list(directory) def get_rpms_list(directory)
Dir.glob(File.join(directory, "**", "*.rpm")).reject { |f| File.directory?(f) || f =~ /repodata\// }.map { |f| f.delete_prefix(directory + "/") } Dir.glob(File.join(directory, "**", "*.rpm")).reject { |f| File.directory?(f) || f =~ /repodata\// }.map { |f| f.delete_prefix(directory + "/") }
end 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

View File

@@ -12,9 +12,12 @@ old = 3
[counter] [counter]
path = "locks/counter" path = "locks/counter"
[build]
path = "locks/build"
[configs] [configs]
hide=open,amazon,anolis,circle,custom,euro,fedora,mageia,navy,alma,rocky hide=open,amazon,anolis,circle,custom,euro,fedora,mageia,navy,rocky
selected=msvsphere selected=alma
[pages] [pages]
items_per_page = 30 items_per_page = 30

View 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

View File

@@ -0,0 +1,6 @@
require "sequel"
Sequel.migration do
change do
end
end

View 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

View File

@@ -0,0 +1,19 @@
# Общий принцип работы с MockGUI
Система является однопользовательской.
Рекомендуется устанавливать ее на локальной машине в локальной сети.
## Текущие ограничения
* Одновременно может запускаться только одна сборка.
* Сборка потребляет все доступные ядра процессора
* На текущий момент система является alpha сборкой, поэтому подавление ошибок не включено, для детального отслеживания ошибок
## Особые указания
Внизу страницы веб интерфейса есть нижняя панель управления. Там имеется два сервисных пункта меню:
`Сбросить buildroots` - со временем, при большом количестве сборок, кэш mock-билдера может разрастаться, для освобождения кэшей рекомендуется периодически запускать сброс с помощью данного пункта меню.
`Подчистка` - проводится инвентаризация гит-проектов и если в базе данных нет записи, то она добавляется. Этот пункт дает возможность подложить гит-проекты и одним нажатием добавить их в веб-интерфейс.

View 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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View 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 |
## Интерфейс
![Главный экран MockGUI](img/mockgui_mainscreen.png)
## Исходные коды
* [github](https://github.com/bayrepo/mock-gui)
* [developers git](https://dev.brepo.ru/brepo/mock-gui)

View 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
View 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
```

View File

@@ -0,0 +1,23 @@
# Обновление
Для обновления сервиса, необходимо перейти в корневой каталог программы под пользователем mockgui:
```shell
cd /home/mockgui/mock-gui/
```
И затем вызвать команду:
```shell
git pull
```
Данна команда обновит системные каталоги, не тронув каталоги с данными.
И затем от привилегированного пользователя вызвать:
```shell
sudo systemctl restart mockgui.service
```
Единственное предупреждение - в момент обновления не должно происходить никаких сборок в MockGUI, они должны быть все завершенные.

View File

@@ -0,0 +1,23 @@
# Список сборок
На данной странице отображается информация о текущих и прошлых сборках
![Список сборок](../img/mockgui_builds1.png)
Если нажать на ссылку "Детальнее", то можно получить детальную информацию о сборке:
![Список сборок2](../img/mockgui_build10.png)
Где можно увидеть все файлы логов с ошибками и без, пакеты собранные при сборке и т.д.
Сборка может находится в таком состоянии:
* Идет сборка - проект собирается
* Ожидает сборки - одновременно может происходить только одна сборка, поэтому все проекты требующие сборки будут ожидать очереди. В этот момент сборку можно отменить
* Отменена - сборка была запланирована, но потом отменена
* Ошибка сборки - сборка завершилась с ошибкой
* Сборка успешно завершена - успешная сборка с пакетами
Список сборок может быть:
* общим
* сборки проекта
* сборки проекта и git репозитория

View 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` поэтому данная папка так же должна быть большого объема.

View File

@@ -0,0 +1,19 @@
# Окружения сборки mock
Отображает список доступных окружений, отфильтрованных согласно [конфигурации](configuration.md)
Файлы конфигурации доступны только для ознакомления.
Добавлять можно только вручную, редактируя файлы по пути `/etc/mock/`
## Список конфигураций
![Список конфигураций](../img/mockgui_env1.png)
## Просмотр файлов конфигураций сборки
![Конфигурация сборки](../img/mockgui_env2.png)
В листинге файла конфигурации сборки можно кликать мышкой на `include` и будет происходить переход на указанный шаблон или конфигурацию.
Такой механизм для ознакомления с конфигурацией сборки.

View File

@@ -0,0 +1,51 @@
# Управление git репозиториями
По-умолчанию git проекты - это папки содержащие bare git структуры, которые расположены в каталоге `repo` (настраивается в [конфигурации](configuration.md))
Папка repo может содержать не зарегистрированные git репозитории, в таком случае они при открытии страницы автоматически добавятся в базу (правда без писания).
## Список git репозиториями
![Первоначальный вид](../img/mockgui_creategit1.png)
![С добавленными репозиториями](../img/mockgui_creategit3.png)
## Создание нового пустого git репозитория
![Создание пустого git репозитория](../img/mockgui_creategit2.png)
Необходимо заполнить имя git репозитория и описание. И нажать кнопку "Создать".
## Просмотр информации git репозитория
На текущий момент есть возможность просмотра:
* Списка веток
* Списка коммитов
* Списка тегов
* Строки для клонирования репозитория
* Описание репозитория
* Списка файлов указанной ветки репозитория
Список тегов и коммитов даст информацию о том, что исходные коды в ветке master находятся в нужно состоянии ис нужными коммитами.
Сборочница производит выборку исходных кодов из master ветки git репозитория.
![Информация о git репозитории](../img/mockgui_creategit4.png)
Пример строки клонирования репозитория:
![Информация о git репозитории](../img/mockgui_creategit5.png)
Для успешного клонирования и наполнения git репозитория, необходимо либо установить публичный ключ для доступа пользователя по ssh или знать пароль к mockgui пользователю по ssh. Вот почему в [инсталляционном руководстве](../install.md) рекомендуется установить пароль для mockgui пользователя.
Вкладка отображения списка файлов репозитория и указанной ветки или HEAD:
![Информация о git репозитории](../img/mockgui_creategit5_1.png)
## Удаление git репозитория
Удаление возможно только в том случае, если данный git репозиторий не имеет ни рецептов сборки, а так же не подключен ни к одному проекту и соответственно, не имеет сборок, указывающих на данный git репозиторий.
При нажатии на кнопку "Удалить" появится окно, где нужно будет написать имя репозитория и нажать кнопку "Удалить" для подтверждения удаления:
![Удаление git репозитория](../img/mockgui_deletegit1.png)

View File

@@ -0,0 +1,173 @@
# Управление проектами
Проекты - это объединения git репозиториев, результат которого - это репозиторий rpm пакетов (подписанный и не подписанный).
Неподписанный репозиторий используется для внутренних сборок, при подключении проекта в другой проект.
Подписанный репозиторий используется для публикации rpm пакетов на внешний сервер (внешний репозиторий).
## Список проектов
![Список проектов пустой](../img/mockgui_project1.png)
![Список проектов](../img/mockgui_projects4.png)
## Создание нового проекта
![Новый проект](../img/mockgui_projects2.png)
* `Название проекта` - уникальное название проекта, оно будет содержаться в названии будущего репозитория, поэтому стоит подходить к названию с умом.
* `Описание` - краткое описание
* `Не публиковать отладочные пакеты и исходные коды` - при установленном флажке в подписанный репозиторий не публикуются пакеты 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 файле, но функционал собираемой программы меняется.
* `Выберите конфигурацию окружения сборки для проекта` - выбирается конфигурация сборки, выбирается один раз при создании проекта и больше потом не меняется. Можно только создать новый проект с другой конфигурацией.
Пример выбора конфигурации. Имеется поле фильтрации.
![Выбор конфигурации](../img/mockgui_projects3.png)
## Информация о проекте
![Проектная информация](../img/mockgui_projects5.png)
`Добавить git репозиторий к проекту` - позволяет добавить к проекту один из существующих git репозиториев.
Необходимо из выпадающего списка выбрать один - нажать кнопку "Добавить".
Тк выглядит добавленный в проект новый git репозиторий.
![Добавленый репозиторий](../img/mockgui_projects6.png)
Пустые репозиторий без исходников добавить в проект нельзя!
`Редактировать конфигурацию сборки` - локальная конфигурация сборочного окружения для mock.
Данный пункт меню позволяет отредактировать корневой файл сборочного окружения.
Здесь условно можно поменять конфигурацию сборки с которой создавался проект, но такой финт лучше не проделывать, особенно если есть уже собранные пакеты.
![Конфигурация сборки](../img/mockgui_projects7.png)
`Добавить внутренний репозиторий из другого проекта` - позволяет связать проекты при сборке, т.е внутренний неподписанный репозиторий одного проекта, становится доступен для сборки текущего.
![Линк на другой проект](../img/mockgui_projects8.png)
`Список всех пакетов` - список пакетов, собранных для данного проекта (неподписанных пакетов)
![Список пакетов](../img/mockgui_projects20.png)
Отображается структура репозитория, где можно перемещаться по папкам и нажимать на rpm пакеты, получая о них информацию.
`Управление снимками репозитория` - это ссылка настраницу, где можно создать снимок `неопубликоанного` репозитория, восстановить пакеты из сохраненного писка или удалить ранее созданные снимки или посмотреть содержимое снимков.
Данная опция полезна при деструктивных изменениях в проекте, например собирается пакет с сомнительным функционалом и лучше перед сборкой создать снимок, чтоб можно было потом откатить изменения обратно.
Пример создания и управления снимками.
Так выглядит страница управления снимками:
![Снимки репозитория](../img/mockgui_projects20_1.png)
Слева список снимков, справа - содержимое выбранного снимка.
Пример отображения содержимого:
![Снимки репозитория](../img/mockgui_projects20_2.png)
Напротив каждого снимка есть занчок "Корзина" и "Стрелочка". Нажимая на корзинку - можно удалить снимок, а на жимая на стрелочку - восстановить пакеты из снимка. При восстановлении, пакеты из неподписанного репозитория удаляются и заменяются пакетами из снимка. Далее их можно переподписать и они переместятся в репозиторий с подписанными пакетами.
Для создания снимка необхоимо нажать на знак "+".
Важно отметить, что для создания, удаления или восстановления снимка открывается дополнительный экран:
![Снимки репозитория](../img/mockgui_projects20_3.png)
Для подтвержения действия недостаточно только нажать на кнопку "Создать", "Удалить", "Восстановить". Важно еще сделать активным флажок.
Этот флажок подтверждает действие окончательно. Если его не установить, то ничего не произойдет и операция не будет выполнена.
`Список сборок проектов` - отображается список сборок, принадлежащих проекту.
В данном списке можно получить информацию о каждой сборке. Детальнее о [сборках](builds.md)
`Подписать` - подписать gpg ключом файлы репозитория проекта и опубликовать их в отдельном репозитории
Как происходит подпись:
![Подпись](../img/mockgui_projects25.png)
Подписывание требует наличие 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. Т.е в таком виде как репозиторий будет выглядеть при публикации в интернете.
![Подписанный репозиторий](../img/mockgui_projects26.png)
`Установить адрес подписанного репозитория` - установить адрес подписанного репозитория, т.е установить 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 будущего удаленного репозитория.
`Удалить старые версии пакетов` - позволяет подчистить накопившиеся старые версии пакетов, остаются только заданное число самых свежив версий.
При нажатии, так же нужно подтвердить намерение удаления и указать, число версий пакета, которые должны быть оставлены. Но нужно быть аккуратным, например некоторые пакеты требуют наличие более старых версий при сборке.
Именно для этого, перед удалением выдается сисок всех пакетов, которые планируются к удалению. И в данном списке можно отменить удаление отдельных пакетов.
`Удалить проект` - удаляется весь проект, подписанные и неподписанные репозитории, сборки.
Но если проект залинкован в другом проекте, то удаление будет невозможным, пока не будет снят линк.
![Пример удаления](../img/mockgui_projects27.png)
Для удаления - нужно перевести в активное состояние флажок и нажать кнопку "Удалить".
Без установки флажка - удаление не произойдет. Такая защита от случайного удаления.
### Список действий над репозиторием проекта
Напротив каждого git репозитория, добавленного в проект есть список действий:
![Расширенный список действий](../img/mockgui_list2.png)
Слева направо:
* удалить git репозиторий из проекта (при этом rpm пакеты в проекте, сборки - остаются)
* обновить исходники проекта - если такой значок появился, значит в git репозиторий в ветку master были внесены изменения и в текущем проекте неактуальные исходники. Когда исходники актуальны список действий выглядит так:
![](../img/mockgui_list1.png)
* запустить сборку - запускается сборка проекта в фоне. Отображается страница, которая обновляется каждые 5 секунд (обновление можно остановить или запустить опять)
![Сборка проекта](../img/mockgui_projects10.png)
Во время сборки отображается лог файл процесса сборки. Результат сборки можно посмотреть на странице [сборки](builds.md)
* указать файл spec для проекта и git репозитория - если в гит репозитории несколько spec файлов, то будет подхватываться первый попавшийся файл, чтоб избежать такого поведения можно точно указать какой файл использовать
![Установка spec файла](../img/mockgui_projects9.png)
* отобразить список сборок для проекта и текущего git репозитория
Если нажать на имя git репозитория в списке, то можно просмотреть его содержимое, т.е список файлов:
![Список файлов репозитория](../img/mockgui_projects14.png)

View 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 файл, извлекает из него версию и имя пакета и создает тут же архив.
Данные рецепты могут изменять исходники, это не вредит репозиторию, т.к все манипуляции делаются с копией данных в временном каталоге.
## Получить список рецептов
![Список рецетов](../img/mockgui_recips1.png)
## Добавить новый рецепт
![Добавить рецепт](../img/mockgui_recip2.png)
`Имя рецепта` - имя файла, который будет создаваться при подготовке сборки во временных исходниках проекта. Имя желательно выбирать такое, чтоб оно не переписало файл исходников
`Описанье` - краткое описание подготовительного скрипта
`Код` - bash скрипт
`git проекты использующие рецепт` - список git-репозиториев, для которых данный рецепт будет применяться. Может иметь несколько значений
## Редактирование существующего рецепта
![Редактирование рецепта](../img/mockgui_recip3.png)
## Удаление рецепта
![Удаление рецепта](../img/mockgui_recip11.png)
Для удаления, нужно нажать кнопку "Удалить" и в появившемся окне вписать имя рецепта для подтверждения удаления.

View File

@@ -0,0 +1,13 @@
# Список пакетов
В данном разделе отображается список git репозиториев и число пакетов собранных из данного репозитория
![Список пакетов](../img/mockgui_rpm1.png)
Для получения более детальной информации нужно нажать на стрелку, и отобразится список всех пакетов, собранных из данного репозитория.
![Список пакетов2](../img/mockgui_rpm2.png)
Здесь же можно получить информацию когда и для какого проекта собирался пакет, а так же детальную информацию об rpm пакете.
![Список пакетов3](../img/mockgui_rpm3.png)

25
docs/mock-gui/mkdocs.yml Normal file
View 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 &copy; 2025 - ... BayRepo
repo_url: https://dev.brepo.ru/brepo/mock-gui

View 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
View File

@@ -0,0 +1,3 @@
---
collections:
- ansible.posix

0
locks/build Normal file
View File

0
logs/empty Normal file
View File

19
mockgui.service Normal file
View 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
View File

0
repo/empty Normal file
View File

View File

@@ -17,13 +17,40 @@
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
<pre> <pre>
echo -e "[<%= repo_name %>] echo -e "[<%= repo_name %>]
name=msvsphere9 repo on repo.brepo.ru name=<%= repo_name %>
skip_if_unavailable=True
baseurl=<%= repo_url %> baseurl=<%= repo_url %>
enabled=1 enabled=1
gpgkey=<%= repo_url %><%= repo_name %>-gpg-key gpgkey=<%= repo_url %><%= repo_name %>-gpg-key
gpgcheck=1" > /etc/yum.repos.d/<%= repo_name %>.repo gpgcheck=1" > /etc/yum.repos.d/<%= repo_name %>.repo
</pre> </pre>
</div> </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>
<div class="container p-3"> <div class="container p-3">
<div class="card border-warning mb-3"> <div class="card border-warning mb-3">
@@ -36,28 +63,47 @@ gpgcheck=1" > /etc/yum.repos.d/<%= repo_name %>.repo
<h2 class="accordion-header"> <h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
data-bs-target="#id<%= index %>" aria-expanded="false" aria-controls="id<%= index %>"> data-bs-target="#id<%= index %>" aria-expanded="false" aria-controls="id<%= index %>">
SRPMS <%= item[1] %>
</button> </button>
</h2> </h2>
<div id="id<%= index %>" class="accordion-collapse collapse" data-bs-parent="#dirInfo"> <div id="id<%= index %>" class="accordion-collapse collapse" data-bs-parent="#dirInfo">
<div class="accordion-body"> <div class="accordion-body">
<div class="accordion accordion-flush" id="rpmInfo"> <div class="accordion accordion-flush" id="rpmInfo">
<% item.each_with_index do |idata, jndex| %> <% item[0].each_with_index do |idata, jndex| %>
<div class="accordion-item"> <div class="accordion-item">
<h2 class="accordion-header"> <h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" <button class="accordion-button collapsed" type="button"
data-bs-toggle="collapse" data-bs-target="#rid<%= jndex %>" aria-expanded="false" data-bs-toggle="collapse" data-bs-target="#rid<%= jndex %>" aria-expanded="false"
aria-controls="rid<%= jndex %>"> aria-controls="rid<%= jndex %>">
<%= idata[:fname] %> (<%= idata[:stat]%>) <%= idata[1] %>
</button> </button>
</h2> </h2>
<div id="rid<%= jndex %>" class="accordion-collapse collapse" data-bs-parent="#rpmInfo"> <div id="rid<%= jndex %>" class="accordion-collapse collapse" data-bs-parent="#rpmInfo">
<div class="accordion-body"> <div class="accordion-body">
<ul class="list-group list-group-flush">
<% idata[:chlog].each do |chlg| %> <div class="accordion accordion-flush" id="pkgInfo">
<li class="list-group-item list-group-item-warning"> - <%= chlg.text %> </li> <% 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 %> <% end %>
</ul> </div>
</div> </div>
</div> </div>
</div> </div>

23
views/buildsclean.erb Normal file
View 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 %>

View File

@@ -8,6 +8,7 @@
<th scope="col" class="text-center">git репозиторий</th> <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>
<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> </tr>
@@ -25,6 +26,12 @@
when 2 when 2
st = "Сборка успешно завершена" st = "Сборка успешно завершена"
cl = "text-bg-success" cl = "text-bg-success"
when 3
st = "Ожидает сборки"
cl = "text-bg-secondary"
when 4
st = "Отменена"
cl = "text-bg-light"
else else
st = "Неизвестно" st = "Неизвестно"
cl = "text-bg-light" 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"><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[:createat] %></td>
<td class="text-center"><%= item[:pkgcnt] %></td> <td class="text-center"><%= item[:pkgcnt] %></td>
<td class="text-center"><%= item[:timeproc] %> сек</td>
<td class="text-center <%= cl %>"> <td class="text-center <%= cl %>">
<%= st %> <%= st %>
</td> </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> </tr>
<% end %> <% end %>
</tbody> </tbody>

View File

@@ -6,7 +6,8 @@
<ul class="list-inline"> <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://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 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>
<ul class="list-inline"> <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"> <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">

View File

@@ -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-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-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-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> </ul>
<div class="tab-content"> <div class="tab-content">
<div class="tab-pane active" role="tabpanel" id="tab-1"> <div class="tab-pane active" role="tabpanel" id="tab-1">
@@ -123,6 +124,34 @@
</div> </div>
</div> </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> </div>
</div> </div>

View File

@@ -45,6 +45,10 @@
<span class="p-1"> <span class="p-1">
<a href="/bldcfg">build-окружения</a> <a href="/bldcfg">build-окружения</a>
</span> </span>
<span>|</span>
<span class="p-1">
<a href="/builds">сборки</a>
</span>
</div> </div>
</div> </div>
<div class="container"> <div class="container">

View File

@@ -9,6 +9,7 @@
<th scope="col" class="text-center">git репозиторий</th> <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>
<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> </tr>
@@ -26,21 +27,36 @@
when 2 when 2
st = "Сборка успешно завершена" st = "Сборка успешно завершена"
cl = "text-bg-success" cl = "text-bg-success"
when 3
st = "Ожидает сборки"
cl = "text-bg-secondary"
when 4
st = "Отменена"
cl = "text-bg-light"
else else
st = "Неизвестно" st = "Неизвестно"
cl = "text-bg-light" cl = "text-bg-light"
end end
%> %> end
<tr> <tr>
<td scope="row"><%= item[:buildid] %></td> <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="/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"><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[:createat] %></td>
<td class="text-center"><%= item[:pkgcnt] %></td> <td class="text-center"><%= item[:pkgcnt] %></td>
<td class="text-center"><%= item[:timeproc] %> сек</td>
<td class="text-center <%= cl %>"> <td class="text-center <%= cl %>">
<%= st %> <%= st %>
</td> </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> </tr>
<% end %> <% end %>
</tbody> </tbody>

View File

@@ -9,6 +9,7 @@
<th scope="col" class="text-center">git репозиторий</th> <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>
<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> </tr>
@@ -26,6 +27,12 @@
when 2 when 2
st = "Сборка успешно завершена" st = "Сборка успешно завершена"
cl = "text-bg-success" cl = "text-bg-success"
when 3
st = "Ожидает сборки"
cl = "text-bg-secondary"
when 4
st = "Отменена"
cl = "text-bg-light"
else else
st = "Неизвестно" st = "Неизвестно"
cl = "text-bg-light" 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"><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[:createat] %></td>
<td class="text-center"><%= item[:pkgcnt] %></td> <td class="text-center"><%= item[:pkgcnt] %></td>
<td class="text-center"><%= item[:timeproc] %> сек</td>
<td class="text-center <%= cl %>"> <td class="text-center <%= cl %>">
<%= st %> <%= st %>
</td> </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> </tr>
<% end %> <% end %>
</tbody> </tbody>

18
views/prjclean1.erb Normal file
View 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
View 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
View 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
View 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 %>

View File

@@ -23,6 +23,14 @@
<% end %> <% end %>
<label class="form-check-label" for="nopublic">Не публиковать отладочные пакеты и исходные коды</label> <label class="form-check-label" for="nopublic">Не публиковать отладочные пакеты и исходные коды</label>
</div> </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"> <div class="mb-3">
<label for="conflist" class="form-label">Выберите конфигурацию окружения сборки для проекта</label> <label for="conflist" class="form-label">Выберите конфигурацию окружения сборки для проекта</label>
<div class="conflist"> <div class="conflist">

View File

@@ -33,6 +33,13 @@
Запрещена публикация пакетов с исходными кодами Запрещена публикация пакетов с исходными кодами
<% end %> <% end %>
</div> </div>
<div class="pb-2">
<% if @proj_tmpbuild == 1 %>
Сборка с добавлением даты и идентификатора
<% else %>
Обычная сборка пакетов
<% end %>
</div>
<div class="list-group"> <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 <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">Редактировать class="bi bi-pen"></i><span class="ms-2">Редактировать
@@ -42,6 +49,8 @@
<a href="/prjrpm/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i <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">Список всех class="bi bi-box"></i><span class="ms-2">Список всех
пакетов</span></a> пакетов</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 <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> 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 <a href="/prjsign/<%= ERB::Util.url_encode(@proj_id) %>" class="list-group-item list-group-item-action list-group-item-dark icon-link"><i
@@ -50,6 +59,8 @@
class="bi bi-file-earmark-diff"></i><span class="ms-2">Просмотр подписанного репозитория</span></a> 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 <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> 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 <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> class="bi bi-trash"></i><span class="ms-2">Удалить проект</span></a>
</div> </div>

42
views/prjshot1.erb Normal file
View 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 %>

17
views/prjshot_add.erb Normal file
View File

@@ -0,0 +1,17 @@
<%= erb :header %>
<div class="container">
<h2 class="text-center">Создание снимка репозитория проекта <%= @proj_name %></h2>
<form action="/prjsnap_add/<%= 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="yes" name="yes">
<label class="form-check-label" for="yes">
Вы уверены, что хотите создать копию текущего репозитория?
</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-success" name="create" value="create">Создать</button>
</div>
</form>
</div>
<%= erb :footer %>

18
views/prjshot_delete.erb Normal file
View File

@@ -0,0 +1,18 @@
<%= erb :header %>
<div class="container">
<h2 class="text-center">Удаление снимка проекта <%= @proj_name %></h2>
<form action="/prjsnap_delete/<%= ERB::Util.url_encode(@proj_id) %>?snap=<%= ERB::Util.url_encode(@snap_name) %>" method="post">
<div class="form-check form-switch text-center pb-3">
<input class="form-check-input" type="checkbox" role="switch" id="yes" name="yes">
<label class="form-check-label" for="yes">
Вы уверены, что хотите удалить снимок <%= @snap_name %>?<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/prjsnap_restore.erb Normal file
View File

@@ -0,0 +1,18 @@
<%= erb :header %>
<div class="container">
<h2 class="text-center">Восстановить пакеты репозитория <%= @proj_name %> из снимка</h2>
<form action="/prjsnap_restore/<%= ERB::Util.url_encode(@proj_id) %>?snap=<%= ERB::Util.url_encode(@snap_name) %>" method="post">
<div class="form-check form-switch text-center pb-3">
<input class="form-check-input" type="checkbox" role="switch" id="yes" name="yes">
<label class="form-check-label" for="yes">
Вы уверены, что хотите восстановить пакеты из снимка <%= @snap_name %>?<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="restore" value="restore">Восстановить</button>
</div>
</form>
</div>
<%= erb :footer %>

View File

@@ -1,12 +1,12 @@
<%= erb :header %> <%= erb :header %>
<div class="container"> <div class="container">
<h2 class="text-center">Подтвердите подпись пакетов проекта <%= @proj_name %></h2> <h2 class="text-center">Установить адрес для репозитория проекта <%= @proj_name %></h2>
<form action="/prjremoteaddr/<%= ERB::Util.url_encode(@proj_id) %>" method="post"> <form action="/prjremoteaddr/<%= ERB::Util.url_encode(@proj_id) %>" method="post">
<div class="form-check form-switch text-center pb-3"> <div class="text-center pb-3">
<input class="form-check-input" type="text" role="switch" id="address" name="address" value="<%= @address %>"> <label class="form-label" for="address">
<label class="form-check-label" for="address"> Укажите адрес удаленного репозитория:
Укажите адресс удаленного репозитория:
</label> </label>
<input class="form-control" type="text" id="address" name="address" value="<%= @addres %>">
</div> </div>
<div class="mb-3 text-center"> <div class="mb-3 text-center">
<button type="submit" class="btn btn-primary" name="cancel" value="cancel">Отменить</button> <button type="submit" class="btn btn-primary" name="cancel" value="cancel">Отменить</button>

Some files were not shown because too many files have changed in this diff Show More