{"id":396279,"date":"2024-06-29T12:44:36","date_gmt":"2024-06-29T12:44:36","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=396279"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-06-29T12:44:36","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=396279","title":{"rendered":"<span>\u041e\u043f\u044b\u0442 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0438\u0437 Gitea \u0432 GitLab. \u0421\u043b\u043e\u0436\u043d\u043e, \u043d\u043e \u0443\u0441\u043f\u0435\u0448\u043d\u043e<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/9a5\/d9e\/e44\/9a5d9ee442d5eb7eadc2f530b37abba3.png\" width=\"780\" height=\"450\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/9a5\/d9e\/e44\/9a5d9ee442d5eb7eadc2f530b37abba3.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u043c\u0438\u0440\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430. \u0420\u0430\u0437\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u043d\u0438 \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b: Git, Mercurial, Bazaar, \u2014 \u0442\u0430\u043a \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b (cloud, self-hosted). \u041d\u043e \u0435\u0441\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u0432\u0430\u0436\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043f\u0440\u0438 \u0438\u0445 \u0432\u044b\u0431\u043e\u0440\u0435: \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c: issue tracker, CI\/CD, wiki \u0438 \u0442.\u0434. \u0422\u0430\u043a \u0441\u043b\u043e\u0436\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043c\u044b \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c GitLab (\u0432\u0430\u0440\u0438\u0430\u043d\u0442 on-premise) \u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0435\u0441\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u0440\u043e\u0442\u0438\u0432, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u0435\u043c\u0443 \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u0440\u043e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0438\u0437 Gitea c Jenkins \u0432 GitLab \u0438 \u043e \u0442\u043e\u043c, \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c Python-\u0441\u043a\u0440\u0438\u043f\u0442\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u0434\u043b\u044f \u0443\u0441\u043f\u0435\u0445\u0430 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p><strong><em>\u0412\u0430\u0436\u043d\u043e!<\/em><\/strong><em> \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442\u0441\u044f Gitea 1.13.4 \u0438 GitLab 13.8. \u0412 \u043d\u043e\u0432\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0442 \u043f\u0435\u0440\u0435\u043d\u043e\u0441, \u043d\u043e \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u044d\u0442\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0431\u044b\u043b\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438.<\/em><\/p>\n<h2>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e Gitea \u0438 \u0437\u0430\u0434\u0430\u0447\u0443<\/h2>\n<p><a href=\"https:\/\/gitea.io\/en-us\/\"><u>Gitea<\/u><\/a> \u2014 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u0430\u044f Open Source-\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Git-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c\u0438. \u042d\u0442\u043e \u0444\u043e\u0440\u043a \u0434\u0440\u0443\u0433\u043e\u0439 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u2014 <a href=\"https:\/\/github.com\/gogs\/gogs\"><u>Gogs<\/u><\/a>. \u041e\u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043e\u0447\u0435\u0442\u0430\u0442\u044c \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0448\u0438\u0440\u043e\u043a\u0438\u043c \u0441\u043f\u0435\u043a\u0442\u0440\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 GitHub-\u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c API.<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 25 \u0442\u044b\u0441\u044f\u0447 \u0437\u0432\u0451\u0437\u0434 \u043d\u0430 <a href=\"https:\/\/github.com\/go-gitea\/gitea\"><u>GitHub<\/u><\/a>. \u0421\u0440\u0435\u0434\u0438 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u043e\u0432 \u2014 DigitalOcean \u0438 Equinix, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c Gitea \u043c\u043e\u0436\u043d\u043e \u043d\u0430 <a href=\"https:\/\/opencollective.com\/gitea\"><u>Open Collective<\/u><\/a> <em>(\u0438 \u043f\u0440\u043e\u0441\u043b\u0435\u0434\u0438\u0442\u044c, \u043a\u0443\u0434\u0430 \u043f\u043e\u0439\u0434\u0443\u0442 \u0432\u0430\u0448\u0438 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430)<\/em>.\u00a0<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2a2\/1c6\/c45\/2a21c6c451e1b72564a296f440834ebe.png\" alt=\"\u041a\u0430\u043a \u0432\u044b\u043b\u044f\u0434\u0438\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Gitea\" title=\"\u041a\u0430\u043a \u0432\u044b\u043b\u044f\u0434\u0438\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Gitea\" width=\"948\" height=\"460\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2a2\/1c6\/c45\/2a21c6c451e1b72564a296f440834ebe.png\"\/><figcaption>\u041a\u0430\u043a \u0432\u044b\u043b\u044f\u0434\u0438\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Gitea<\/figcaption><\/figure>\n<p>\u0418\u0437 \u043f\u043b\u044e\u0441\u043e\u0432 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e Gitea \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430 \u043a\u0430\u043a \u0432 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435, \u0442\u0430\u043a \u0438 \u0432 \u0431\u044d\u043a\u0430\u043f\u0435. \u0421\u0438\u0441\u0442\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435, \u0438 \u043e\u043d\u0430 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b: wiki-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u0442.\u0435. todo-\u043b\u0438\u0441\u0442\u044b.<\/p>\n<p>\u041d\u043e \u044d\u0442\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443. \u0412 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e CI\/CD, \u0438 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u0438\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u0432 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u044d\u0442\u0443 \u0440\u043e\u043b\u044c \u0438\u0433\u0440\u0430\u043b Jenkins, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 <a href=\"https:\/\/github.com\/jenkinsci\/gitea-plugin\"><u>\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440 \u0431\u044b\u043b \u0441\u043a\u043e\u0440\u0435\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043d\u0430\u0441\u043b\u0435\u0434\u0438\u0435\u043c, \u0447\u0435\u043c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e. CI\/CD \u0441 \u043d\u0438\u043c \u0431\u044b\u043b \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u0435\u043d \u0432 \u0440\u0430\u0431\u043e\u0442\u0435. \u0414\u043b\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043f\u043b\u043e\u044f \u043c\u044b \u0441\u043e\u0448\u043b\u0438\u0441\u044c \u043d\u0430 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 GitLab, \u0430 \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e \u0438 \u0437\u0430\u043c\u0435\u043d\u0443 \u0441\u0430\u043c\u043e\u0439 Gitea, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0442\u0435\u0440\u044f\u043b\u0438 \u0441\u043c\u044b\u0441\u043b. \u0422\u0430\u043a\u0436\u0435 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442 \u0431\u044b\u043b\u0438 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u043c\u0435\u043b\u043a\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u0435\u0448\u0430\u043b\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438.<\/p>\n<h3>\u0427\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435<\/h3>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u2014 Gitea 1.13.4 \u0441\u043e 165 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c\u0438 \u0438 94 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.\u00a0\u0412\u0441\u0451 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d\u043e \u043f\u043e 18 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438, \u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0431\u044b\u043b\u0438 \u043b\u0438\u0447\u043d\u044b\u043c\u0438.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u0445\u043e\u0442\u0435\u043b \u0442\u0435\u0440\u044f\u0442\u044c \u0438\u0441\u0442\u043e\u0440\u0438\u044e pull requests, \u0430 \u0438\u0445 \u0431\u044b\u043b\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u043d\u043e\u0433\u043e: \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u0445 \u2014 \u0431\u043e\u043b\u0435\u0435 5 \u0442\u044b\u0441\u044f\u0447.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/a42\/792\/90f\/a4279290fa21207feb0ed744586b5245.png\" alt=\"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u0438 Gitea \u0434\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430\" title=\"\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u0438 Gitea \u0434\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430\" width=\"1131\" height=\"128\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a42\/792\/90f\/a4279290fa21207feb0ed744586b5245.png\"\/><figcaption>\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u0438 Gitea \u0434\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430<\/figcaption><\/figure>\n<p>\u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0441\u0430\u043c\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0438 \u0433\u0440\u0443\u043f\u043f \u0431\u044b\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0443\u0436 \u0438 \u043c\u043d\u043e\u0433\u043e, \u00ab\u043d\u0430\u043a\u043b\u0438\u043a\u0438\u0432\u0430\u0442\u044c\u00bb \u0442\u0430\u043a\u043e\u0435 (\u0434\u0430 \u0435\u0449\u0451 \u0438 \u0431\u0435\u0437 \u043e\u0448\u0438\u0431\u043e\u043a) \u2014 \u044d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0434\u043e\u043b\u0433\u043e.\u00a0\u00a0<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/366\/5b5\/726\/3665b5726ab9a56d896a6b96f09b0f14.png\" width=\"346\" height=\"394\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/366\/5b5\/726\/3665b5726ab9a56d896a6b96f09b0f14.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0414\u0430 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435, \u043c\u044b \u0436\u0435 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u044b! \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0448\u043b\u0438 \u043f\u0443\u0442\u0435\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043d\u0438\u0436\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u043e\u0434\u043e\u043b\u0435\u0442\u044c. \u041f\u043e\u043f\u0443\u0442\u043d\u043e \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 REST API \u043e\u0431\u043e\u0438\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439: \u0438 Gitea, \u0438 GitLab. \u041f\u043e\u0441\u0435\u043c\u0443 \u044d\u0442\u043e\u0442 \u043e\u043f\u044b\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043d\u043e \u0438 \u043f\u043e\u0432\u0441\u0435\u0434\u043d\u0435\u0432\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f: \u0438\u043c\u043f\u043e\u0440\u0442, \u044d\u043a\u0441\u043f\u043e\u0440\u0442, \u043f\u0435\u0440\u0435\u043d\u043e\u0441<\/h2>\n<p>\u0418\u0442\u0430\u043a, \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0430\u043c\u043e\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438. \u041e\u0431\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u043c\u0435\u044e\u0442 \u0440\u0430\u0437\u0432\u0438\u0442\u044b\u0439 <a href=\"https:\/\/docs.gitea.io\/en-us\/api-usage\/\"><u>API<\/u><\/a>. \u0412\u044b\u0431\u0435\u0440\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API \u043e\u0431\u0435\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c:<\/p>\n<ul>\n<li>\n<p>\u0414\u043b\u044f Gitea \u044f \u0432\u044b\u0431\u0440\u0430\u043b Python-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 <a href=\"https:\/\/github.com\/dblueai\/giteapy\"><u>giteapy<\/u><\/a>. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043e\u043d \u043d\u0435 \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448, \u043a\u0430\u043a \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0439 \u0434\u043b\u044f GitLab. <em>(\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043f\u043e\u0432\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u0435\u0449\u0451 \u0432\u0441\u0442\u0440\u0435\u0442\u044f\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u043c\u0430\u0440\u043a\u0438.)<\/em><\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f GitLab \u2014 <a href=\"https:\/\/github.com\/python-gitlab\/python-gitlab\"><u>python-gitlab<\/u><\/a>.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0430\u0441\u0442\u044c 1. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438<\/h3>\n<p>\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a GitLab \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0435\u0434\u0438\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">import gitlab  gl = gitlab.Gitlab('https:\/\/gitlab.example.com', private_token='secret')  gl_users = gl.users.list(page=1, per_page=1000)<\/code><\/pre>\n<p>\u0412 giteapy \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 API \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441. \u0418\u0445 \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u2014 6, \u043d\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e 4: <code>AdminApi<\/code>, <code>OrganizationApi<\/code>, <code>RepositoryApi<\/code>, <code>UserApi<\/code>. \u0412\u043e\u0442 \u043a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u043e\u0434 <em>(\u043f\u043e\u043b\u043d\u044b\u0435 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0435 \u043b\u0438\u0441\u0442\u0438\u043d\u0433\u0438 \u2014 \u0441\u043c. \u0432 <\/em><a href=\"https:\/\/github.com\/flant\/examples\/tree\/master\/2021\/09-gitea-gitlab-migration\"><em><u>\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 flant\/examples<\/u><\/em><\/a><em>)<\/em>:<\/p>\n<pre><code class=\"python\">import giteapy configuration = giteapy.Configuration() configuration.api_key['access_token'] = 'secret' configuration.host = 'https:\/\/git.example.com\/api\/v1'  admin_api = giteapy.AdminApi(giteapy.ApiClient(configuration)) user_api_instance = giteapy.UserApi(giteapy.ApiClient(configuration)) org_api_instance = giteapy.OrganizationApi(giteapy.ApiClient(configuration)) repo_api_instance = giteapy.RepositoryApi(giteapy.ApiClient(configuration))  gt_users = admin_api_instance.admin_get_all_users()<\/code><\/pre>\n<p>\u0418 \u0443\u0436\u0435 \u0442\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u043b \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u044e\u0440\u043f\u0440\u0438\u0437:<\/p>\n<pre><code class=\"python\">{'avatar_url': 'https:\/\/gitea.example.com\/user\/avatar\/user1\/-1',  'created': datetime.datetime(2018, 10, 11, 19, 0, 0, tzinfo=tzutc()),  'email': 'user1@example.com',  'full_name': 'User Name',  'id': 2,  'is_admin': False,  'language': 'ru-RU',  'last_login': datetime.datetime(2020, 10, 19, 8, 0, 0, tzinfo=tzutc()),  'login': 'user1'}<\/code><\/pre>\n<p>\u0412\u044b \u0441\u043f\u0440\u043e\u0441\u0438\u0442\u0435: \u0447\u0442\u043e \u0436\u0435 \u043d\u0435 \u0442\u0430\u043a? \u041e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e: \u0432 \u0432\u044b\u0432\u043e\u0434\u0435 API \u043d\u0435 \u0432\u0438\u0434\u043d\u043e, <strong>\u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438\u043b\u0438 \u043d\u0435\u0442<\/strong>. \u0418\u0437\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u0440\u0438\u0432\u0435\u043b\u043e \u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u0431\u0430\u0437\u044b Gitea. \u0411\u043b\u0430\u0433\u043e, \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a\u0430\u044f \u0443\u0436 \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u2014 \u043e\u043d\u0430 \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c:<\/p>\n<pre><code class=\"python\">SELECT is_active FROM \u201cuser\u201d WHERE id = &lt;user_id><\/code><\/pre>\n<p>\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u044b\u043b\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e (\u043e\u043a\u043e\u043b\u043e 10), \u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u0438\u0445 \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<h4>SSH-\u043a\u043b\u044e\u0447\u0438<\/h4>\n<p>\u041b\u043e\u0433\u0438\u0447\u043d\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043d\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0435\u0449\u0435 \u0438 SSH-\u043a\u043b\u044e\u0447\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 API Gitea \u043e\u043f\u0438\u0441\u0430\u043d \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/dblueai\/giteapy\/blob\/master\/docs\/UserApi.md#user_current_get_key\"><u>user_current_get_key<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<ul>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0435\u0439, \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447;<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u043d\u0435\u0442 \u2014 \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 404.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u043c \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0435 \u043c\u044b \u044d\u0442\u043e \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e \u043d\u0435 \u0443\u0447\u043b\u0438 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 API \u043a\u0430\u043a \u0435\u0441\u0442\u044c. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0431\u044b\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 \u2014 \u0438\u0445 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u043b\u043e. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/dblueai\/giteapy\/blob\/master\/docs\/UserApi.md#user_list_keys\"><u>user_list_keys<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u0438 \u0441 \u043d\u0438\u043c \u043d\u0430\u0441 \u0436\u0434\u0430\u043b \u043f\u043e\u0434\u0432\u043e\u0445: \u0432 \u0431\u0430\u0437\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 <em>\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439<\/em> \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e fingerprint.<\/p>\n<pre><code class=\"python\">Indexes:     \"public_key_pkey\" PRIMARY KEY, btree (id)     \"IDX_public_key_fingerprint\" btree (fingerprint)     \"IDX_public_key_owner_id\" btree (owner_id)<\/code><\/pre>\n<p>\u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0448\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043d\u044b\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 \u0432 GitLab. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u044d\u0442\u0438 \u043a\u043b\u044e\u0447\u0438 \u0431\u044b\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0443 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043f\u0440\u0438\u043d\u044f\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0438 \u0443 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u044b. \u0412 GitLab \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\"># clean blocked users keys for block_gl_user in gl.users.list(blocked=True, page=1, per_page=10000):     print(\"Blocker user\", block_gl_user.username)     for block_gl_user_key in block_gl_user.keys.list():         print(\"Found key\", block_gl_user_key.title)         block_gl_user_key.delete()<\/code><\/pre>\n<h4>\u041f\u0440\u0430\u0432\u0430<\/h4>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0448\u0430\u0433 \u2014 \u0432\u044b\u0434\u0430\u0447\u0430 \u0432\u0435\u0440\u043d\u044b\u0445 \u043f\u0440\u0430\u0432. <em>\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438<\/em> Gitea \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 <em>\u0433\u0440\u0443\u043f\u043f\u044b<\/em> GitLab, \u0430 <em>\u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/em> \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 <em>\u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u0432<\/em>.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u0432\u0441\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438\u0437 API, \u043c\u044b \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043b\u0438 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c <strong>\u043c\u0430\u0442\u0440\u0438\u0446\u0443 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432<\/strong>:<\/p>\n<pre><code class=\"python\"># map access rules map_access = {'Owners': gitlab.OWNER_ACCESS,               'Developers': gitlab.DEVELOPER_ACCESS,               'QA': gitlab.DEVELOPER_ACCESS,               'Manager':gitlab.REPORTER_ACCESS,               'Managers': gitlab.REPORTER_ACCESS,               'Dev': gitlab.DEVELOPER_ACCESS,               'Services': gitlab.REPORTER_ACCESS,               'services': gitlab.REPORTER_ACCESS}  # inspect Gitea orgs and create Gitlab groups # get all orgs gt_all_orgs = admin_api_instance.admin_get_all_orgs() for gt_org in gt_all_orgs:     # does the group exist?     res = None     try:         res = gl.groups.get(gt_org.username)     except:         pass      if res:         # append existing groups to dictionary          dict_gl_groups[gt_org.username] = res     else:         # create the missing group         gl_group = gl.groups.create({'name': gt_org.username, 'path': gt_org.username})         if len(gt_org.description) > 0:             gl_group.description = gt_org.description         if len(gt_org.full_name) > 0:             gl_group.full_name = gt_org.full_name         gl_group.save()         dict_gl_groups[org.username] = gl_group     # list teams for the Gitea org     gt_org_teams = org_api_instance.org_list_teams(gt_org.username)     for team in teams:         # get all team members         members = org_api_instance.org_list_team_members(team.id)         for user in members:             # add members to groups with their access level             # dict_gl_users was created on user creation step             member = dict_gl_groups[gt_org.username].members.create({'user_id': dict_gl_users[user.login].id, 'access_level': map_access.get(team.name, gitlab.REPORTER_ACCESS)})<\/code><\/pre>\n<h4>\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u0438\u0445 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0439? \u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u00ab\u043f\u043e\u0440\u043e\u0434\u0438\u0442\u00bb \u043c\u0430\u0441\u0441\u0443 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439: \u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0442.\u043f. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0440\u0435\u0448\u0438\u0442\u044c, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u0440\u0440\u0435\u0441\u043f\u043e\u043d\u0434\u0435\u043d\u0446\u0438\u044e \u0438\u043b\u0438 \u0436\u0435 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432 \u0447\u0451\u0440\u043d\u0443\u044e \u0434\u044b\u0440\u0443.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u0443 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u043e\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0430\u0434\u0438\u0442\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">gl_user = gl.users.create({'email': gt_user.email,                                    'password': password,                                    'username': gt_user.login,                                    'name': gt_user.full_name if len(gt_user.full_name) > 0 else gt_user.login,                                    'admin': gt_user.is_admin,                                    'skip_confirmation': True})<\/code><\/pre>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 <code>\/dev\/null<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 <a href=\"https:\/\/www.vanimpe.eu\/2012\/04\/03\/run-postfix-as-a-blackhole-mail-server-open-relay\/\"><u>\u043a\u043e\u043d\u0444\u0438\u0433 Postfix<\/u><\/a>:<\/p>\n<pre><code class=\"python\">relayhost =  relay_transport = relay relay_domains = static:ALL smtpd_end_of_data_restrictions = check_client_access static:discard<\/code><\/pre>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043b\u0438\u0435\u043d\u0442 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0432 \u00ab\u0447\u0435\u0440\u043d\u0443\u044e \u0434\u044b\u0440\u0443\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430. \u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f <code>skip_confirmation<\/code>, \u0442\u043e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c, \u0435\u0441\u043b\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u044d\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044d\u0442\u043e <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab\/-\/issues\/212539#:~:text=go%20to%20the%20Admin%20Area,block%20will%20be%20still%20shown.\"><u>\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u0431\u0430\u0433<\/u><\/a> GitLab: \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab\/-\/issues\/212539#note_323746089\"><u>\u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f<\/u><\/a> \u043b\u0435\u0437\u0442\u044c \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c Rails.<\/p>\n<h4>\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u0438\u0442\u043e\u0433\u0438<\/h4>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0432 Gitea \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u044d\u0442\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439:<\/p>\n<ol>\n<li>\n<p>\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0432\u0430\u0436\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 API;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b API \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0443\u0431\u043b\u0438\u0440\u0443\u044e\u0449\u0438\u0435\u0441\u044f \u043a\u043b\u044e\u0447\u0438.<\/p>\n<\/li>\n<\/ol>\n<p>\u0421 \u0434\u0440\u0443\u0433\u043e\u0439 \u0441\u0442\u043e\u0440\u043e\u043d\u044b, \u0432 GitLab \u0435\u0441\u0442\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0441 \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435\u043c email. \u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0441\u043a\u0440\u0438\u043f\u0442 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u0432 \u043d\u0430\u0448\u0435\u043c <a href=\"https:\/\/github.com\/flant\/examples\/tree\/master\/2021\/09-gitea-gitlab-migration\"><u>\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0441 \u043f\u0440\u0438\u043c\u0435\u0440\u0430\u043c\u0438<\/u><\/a>.<\/p>\n<h3>\u0427\u0430\u0441\u0442\u044c 2. \u0420\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/h3>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0433\u043e\u0442\u043e\u0432\u043e \u0434\u0435\u0440\u0435\u0432\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043c\u043e\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0441\u0442\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432. GitLab \u0443\u043c\u0435\u0435\u0442 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0435\u043a\u0442\u044b Gitea \u0443\u0436\u0435 \u0441 \u0432\u0435\u0440\u0441\u0438\u0438 8.15. \u041e\u0434\u043d\u0430\u043a\u043e \u0432\u0441\u0451 \u043d\u0435 \u0442\u0430\u043a \u043f\u0440\u043e\u0441\u0442\u043e, \u043a\u0430\u043a \u0445\u043e\u0442\u0435\u043b\u043e\u0441\u044c \u0431\u044b.<\/p>\n<p>\u041d\u0430\u0447\u043d\u0451\u043c \u0441 \u0442\u043e\u0433\u043e, \u0447\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u043c\u044b \u0437\u0430\u0432\u0435\u043b\u0438 \u0432 Gitea \u0434\u043b\u044f \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u0432\u043e \u0432\u0441\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438. \u0412 \u044d\u0442\u043e\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0442\u0430\u043a\u043e\u0439 \u0441\u043a\u0440\u0438\u043f\u0442.<\/p>\n<pre><code class=\"python\">all_orgs = admin_api_instance.admin_get_all_orgs() for org in all_orgs:     for repo in org_api_instance.org_list_repos(org.username):         body = giteapy.AddCollaboratorOption()         repo_api_instance.repo_add_collaborator(repo.owner.login, repo.name, 'import_user', body=body)      teams = org_api_instance.org_list_teams(org.username)     for team in teams:         members = org_api_instance.org_list_team_members(team.id)         for user in members:             for repo in user_api_instance.user_list_repos(user.login):                 repo_api_instance.repo_add_collaborator(repo.owner.login, repo.name, 'import_user', body=body)<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u043e\u043f\u0443\u0449\u0435\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a API, \u0442\u0430\u043a \u043a\u0430\u043a \u043f\u0440\u0438\u043c\u0435\u0440 \u0431\u044b\u043b \u0443\u0436\u0435 \u0432\u044b\u0448\u0435. \u041c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c \u0435\u0449\u0435 \u043e\u0434\u043d\u0443 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043a Gitea: \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445 \u043f\u043e\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043b\u0438\u0431\u043e <em>User ID<\/em>, \u043b\u0438\u0431\u043e <em>User Login<\/em>. \u041c\u0435\u0441\u0442\u0430\u043c\u0438 \u044d\u0442\u043e \u043d\u0435\u0443\u0434\u043e\u0431\u043d\u043e, \u0442.\u043a. \u0442\u0440\u0435\u0431\u0443\u0435\u0442 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u043e \u0441\u0432\u0435\u0440\u044f\u0442\u044c\u0441\u044f \u0441 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0435\u0439.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0432\u0438\u0434\u043d\u044b \u0432 API, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u043f\u0440\u043e\u0431\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0447\u0430\u0442\u044c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0445 \u0432 GitLab. \u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0441\u043b\u043e\u0436\u043d\u044b\u043c: <a href=\"https:\/\/docs.gitlab.com\/ee\/user\/project\/import\/gitea.html\"><u>\u0437\u0430\u0439\u0442\u0438 \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043d\u043e\u0432\u043e\u0433\u043e \u043f\u0440\u043e\u0435\u043a\u0442\u0430, \u043d\u0430\u0436\u0430\u0442\u044c \u043a\u043d\u043e\u043f\u043a\u0443 \u0438\u043c\u043f\u043e\u0440\u0442\u0430<\/u><\/a>\u2026 \u043d\u043e \u0442\u0430\u043a \u043d\u0438\u0447\u0435\u0433\u043e \u043d\u0435 \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u0442\u0441\u044f. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u0432\u0441\u0442\u0440\u0435\u0447\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u0448\u0430\u0433\u0443.<\/p>\n<h4>nginx \u043a\u0430\u043a \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u0434\u0432\u0443\u0445 \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0432 API<\/h4>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0441\u0442\u043e\u0438\u0442 \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e GitLab \u0437\u043d\u0430\u0435\u0442, \u0447\u0442\u043e API Gitea \u0443\u0441\u0442\u0440\u043e\u0435\u043d \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e API GitHub, \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/github.com\/octokit\/octokit.rb\"><u>Octokit Gem<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u0438\u043c\u043f\u043b\u0435\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u044f API \u0443 \u043d\u0430\u0441 \u043d\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u043e\u043b\u043d\u043e\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u043c\u043f\u043e\u0440\u0442 \u043f\u0435\u0440\u0438\u043e\u0434\u0438\u0447\u0435\u0441\u043a\u0438 \u0441\u043f\u043e\u0442\u044b\u043a\u0430\u0435\u0442\u0441\u044f. \u0415\u0441\u0442\u044c 2 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043c\u043e\u043c\u0435\u043d\u0442\u0430:<\/p>\n<ol>\n<li>\n<p>\u041e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 <code>rate_limit<\/code> \u0432 API;<\/p>\n<\/li>\n<li>\n<p>\u041f\u0443\u0442\u0430\u043d\u0438\u0446\u0430 \u0441 \u043f\u0443\u0442\u044f\u043c\u0438, \u043a\u043e\u0433\u0434\u0430 Octokit \u043f\u044b\u0442\u0430\u043b\u0441\u044f \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043c\u0443\u0441\u043e\u0440\u043d\u044b\u0439 \u043f\u0440\u0435\u0444\u0438\u043a\u0441 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u044b.<\/p>\n<\/li>\n<\/ol>\n<p>\u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u0438\u043d\u0441\u0442\u0430\u043d\u0441 Gitea \u0431\u044b\u043b \u0437\u0430 \u0440\u0435\u0432\u0435\u0440\u0441\u043d\u044b\u043c \u043f\u0440\u043e\u043a\u0441\u0438 \u043d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 nginx, \u0442\u0430\u043a \u0447\u0442\u043e \u0443\u0434\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u0435\u0433\u043e \u043a\u043e\u043d\u0444\u0438\u0433\u0443\u0440\u0430\u0446\u0438\u044e, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u043e\u0439\u0442\u0438 \u044d\u0442\u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u0440\u0430\u0437\u0431\u0435\u0440\u0435\u043c\u0441\u044f \u0441 rate limit. \u042d\u0442\u043e \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 Octokit \u0441\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0435\u0442, \u043a\u0430\u043a \u0447\u0430\u0441\u0442\u043e \u043e\u043d \u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432 API. \u041f\u0440\u0438 \u0437\u0430\u043f\u0440\u043e\u0441\u0435 \u043a \u043a\u043e\u0440\u043d\u0435\u0432\u044b\u043c \u043c\u0435\u0442\u043e\u0434\u0430\u043c Gitea \u043e\u0442\u0434\u0430\u0451\u0442 404, \u0447\u0442\u043e \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442\u0441\u044f \u043a\u0430\u043a <code>Unimplemented<\/code>:<\/p>\n<pre><code class=\"python\">[13\/Apr\/2021:01:08:15 +0000] \"GET \/api\/v1\/rate_limit HTTP\/1.1\" 404 152 \"-\" \"Octokit Ruby Gem 4.15.0\"<\/code><\/pre>\n<p>\u041e\u0434\u043d\u0430\u043a\u043e \u0437\u0430\u043f\u0440\u043e\u0441 \u043a RepoApi \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 401, \u0438\u0437-\u0437\u0430 \u0447\u0435\u0433\u043e \u0438\u043c\u043f\u043e\u0440\u0442 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f:<\/p>\n<pre><code class=\"python\">[13\/Apr\/2021:01:26:25 +0000] \"GET \/org1\/project1.git\/api\/v1\/rate_limit HTTP\/1.1\" 401 152 \"-\" \"Octokit Ruby Gem 4.15.0\"<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u043e\u0431\u043e\u0439\u0442\u0438 \u044d\u0442\u043e, \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 location \u0432 nginx:<\/p>\n<pre><code class=\"python\">location ~* \"\\\/api\\\/v1\\\/rate_limit$\" { \u00a0\u00a0return 404; }<\/code><\/pre>\n<p>\u0412\u0441\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0432\u0435\u0440\u043d\u0443\u0442 404 \u2014 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u043f\u043e\u0439\u0434\u0451\u0442 \u0431\u0435\u0437 \u043f\u0440\u043e\u0431\u043b\u0435\u043c.<\/p>\n<p>\u0412\u0442\u043e\u0440\u0430\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b \u0431\u044b\u043b\u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0435\u0435: Octokit \u0434\u0435\u043b\u0430\u043b \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0441 \u043c\u0443\u0441\u043e\u0440\u043d\u044b\u043c \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u043e\u043c. \u0414\u043e\u043f\u0443\u0441\u0442\u0438\u043c, \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c <code>org1<\/code> \u0441 <code>project1<\/code> \u0438 \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c <code>i.ivanov<\/code> \u0441 <code>project2<\/code> \u0432 \u043b\u0438\u0447\u043d\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 \u0438\u043c\u0451\u043d. \u0412 \u043b\u043e\u0433\u0435 nginx \u043f\u043e\u044f\u0432\u044f\u0442\u0441\u044f \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b:<\/p>\n<pre><code class=\"python\">\/org1\/project1\/api\/v1\/repos\/org1\/project1\/labels?page=1&amp;per_page=100 \/org1\/project1\/api\/v1\/repos\/org1\/project1\/milestones?page=1&amp;per_page=100&amp;state=all \/org1\/project1\/api\/v1\/users\/i.ivanov \/i.\/api\/v1\/repos\/i.ivanov\/project2\/labels?page=1&amp;per_page=100 \/i.ivanov\/project2\/api\/v1\/rate_limit<\/code><\/pre>\n<p>\u041b\u0435\u0433\u043a\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0435\u0439 Octokit \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u043f\u0440\u0435\u0444\u0438\u043a\u0441 <code>\/org1\/project1<\/code>, \u0430 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u043c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u043c \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b 2 \u043f\u0440\u0435\u0444\u0438\u043a\u0441\u0430:<\/p>\n<ul>\n<li>\n<p><code>\/i.<\/code><\/p>\n<\/li>\n<li>\n<p><code>\/i.ivanov\/project2<\/code><\/p>\n<\/li>\n<\/ul>\n<p>\u0412 \u043e\u0431\u0449\u0435\u043c, \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c rewrite, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b:<\/p>\n<pre><code class=\"python\">rewrite '^\\\/([^\\\/]+)\\\/([^\\\/]+\\.git)\\\/api\\\/v1\\\/repos\\\/([^\\\/]+)\\\/([^\\\/]+)\\\/([^\\\/]+)$' \/api\/v1\/repos\/$3\/$4\/$5; rewrite '^\\\/([^\\\/]+)\\\/([^\\\/]+\\.git)\\\/api\\\/v1\\\/users\\\/(.+)$' \/api\/v1\/users\/$3;<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u043d\u044b\u0435 \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u043f\u0438\u0441\u0430\u043d\u044b \u0438, \u043d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e, \u0438\u043c\u043f\u043e\u0440\u0442 \u043f\u0440\u043e\u0448\u0435\u043b \u0443\u0441\u043f\u0435\u0448\u043d\u043e!<\/p>\n<h4>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u043c\u043f\u043e\u0440\u0442<\/h4>\n<p>\u041e\u0441\u0442\u0430\u043d\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u044f\u044f \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430: \u0443 \u043d\u0430\u0441 160 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0434\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432 \u0432\u0435\u0440\u043d\u044b\u0435 namespaces. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u0438\u043c\u043f\u043e\u0440\u0442 \u0447\u0435\u0440\u0435\u0437 API <strong>\u043d\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e\u043b\u043d\u044b\u0439 \u0438\u043c\u043f\u043e\u0440\u0442<\/strong> \u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442 \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u043c\u043f\u043e\u0440\u0442 \u0430\u0440\u0445\u0438\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0435 \u0434\u0430\u0451\u0442 \u0437\u0430\u0433\u0440\u0443\u0437\u0438\u0442\u044c merge requests, issues \u0438 \u0434\u0440\u0443\u0433\u0438\u0435 \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0432\u0435\u0449\u0438. \u041f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b \u0441 GitLab WebUI \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u043b \u0437\u0430\u043f\u0440\u043e\u0441\u044b \u043d\u0430 \u0438\u043c\u043f\u043e\u0440\u0442.<\/p>\n<p>\u042f \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0435\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u043a\u0440\u0438\u043f\u0442 \u0434\u043b\u044f Selenium:<\/p>\n<pre><code class=\"python\">from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import os import time  GITLAB_URL=\"https:\/\/gitlab.example.com\/\" GITLAB_USER=\"user\" GITLAB_PASSWORD=\"pa$$word\" GITED_URL=\"https:\/\/gitea.example.com\/\" GITED_TOKEN=\"superSecret\"   driver = webdriver.Firefox(os.getcwd()+os.path.sep) driver.get(GITLAB_URL)  # Gitlab login user = driver.find_element_by_id(\"user_login\") user.send_keys(GITLAB_USER) pas = driver.find_element_by_id(\"user_password\") pas.send_keys(GITLAB_PASSWORD) login = driver.find_element_by_name(\"commit\").click()<\/code><\/pre>\n<p>\u0417\u0430\u0442\u0435\u043c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u0443\u044e \u0441\u0435\u0441\u0441\u0438\u044e, \u0437\u0430\u043f\u0440\u043e\u0441\u0438\u043c \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0438\u043c\u043f\u043e\u0440\u0442\u0430:<\/p>\n<pre><code class=\"python\"># Starting import process driver.get(GITLAB_URL+\"\/import\/gitea\/new\") gitea_host = driver.find_element_by_name(\"gitea_host_url\") gitea_host.send_keys(GITED_URL) gitea_token = driver.find_element_by_name(\"personal_access_token\") gitea_token.send_keys(GITED_TOKEN) process = driver.find_element_by_name(\"commit\").click()<\/code><\/pre>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0430\u0447\u043d\u0451\u043c \u0438\u043c\u043f\u043e\u0440\u0442 \u0432\u0441\u0435\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0435\u0432. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a Gitea \u0438 \u043d\u0430\u0447\u043d\u0451\u043c \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438:<\/p>\n<pre><code class=\"python\"># iterate over table and import repos step by step wait = WebDriverWait(driver, 10) table =  wait.until(EC.presence_of_element_located((By.XPATH, '\/\/table'))) for row in table.find_elements_by_xpath(\".\/\/tr\"):   group=row.get_attribute(\"data-qa-source-project\").split(\"\/\")[0]   # clicking select button to show dropdown menu and activate buttons   row.find_element_by_class_name(\"gl-dropdown-toggle\").click()   time.sleep(1)   # Finding project group   for btn in row.find_elements_by_class_name(\"dropdown-item\"):     if btn.get_attribute(\"data-qa-group-name\") == group:       btn.click()   time.sleep(1)   # starting import   import_button = row.find_element(By.XPATH, \"\/\/button[@data-qa-selector='import_button']\")   import_button.click()   while True:     time.sleep(10)     # Wait until      status = row.find_elements_by_class_name(\"gl-p-4\")[-1].text     if status == \"Complete\":       break<\/code><\/pre>\n<p>\u041a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0431\u044b, \u0432\u043e\u0442 \u0438 \u0432\u0441\u0451! \u041d\u043e \u044d\u0442\u043e, \u043a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043d\u0435 \u0442\u0430\u043a.<\/p>\n<h4>\u0424\u0438\u043d\u0430\u043b\u044c\u043d\u044b\u0439 \u0448\u0442\u0440\u0438\u0445<\/h4>\n<p>\u041f\u043e\u0441\u043b\u0435 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u043c\u044b \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0438\u043b\u0438 GitLab \u043a Jenkins \u0438 \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u043e\u0448\u0438\u0431\u043a\u0443:<\/p>\n<pre><code class=\"python\">fatal: couldn't find remote ref refs\/merge-requests\/184\/head<\/code><\/pre>\n<p>\u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043f\u0440\u0438 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0435 \u0431\u044b\u043b\u0438 \u043f\u043e\u0442\u0435\u0440\u044f\u043d\u044b Git references. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u043c\u044b \u0440\u0435\u0448\u0438\u043b\u0438 \u0432\u043e \u0432\u0441\u0435 \u0432\u0435\u0442\u043a\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c\u0435\u044e\u0442 \u043e\u0442\u043a\u0440\u044b\u0442\u044b\u0435 merge requests, \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u0443\u0441\u0442\u044b\u0435 \u043a\u043e\u043c\u043c\u0438\u0442\u044b. \u042d\u0442\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u0435\u0440\u0435\u0441\u043e\u0437\u0434\u0430\u0441\u0442 references.<\/p>\n<p>\u0412\u043e\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0442\u0430\u043a\u043e\u0433\u043e workaround:<\/p>\n<pre><code class=\"python\">shutil.rmtree('code',ignore_errors=True)  all_orgs = gl.groups.list() skip_orgs = ['org1','org2'] for org in all_orgs:     if org.name in skip_orgs:         print(\"Skip group\", org.name)         continue     projects = org.projects.list(all=True)     for project in projects:         id=project.id         mrs=gl.projects.get(id=id).mergerequests.list(state='opened', sort='desc',page=1, per_page=10000)         os.mkdir('code')         print(subprocess.run([\"git\", \"clone\", project.ssh_url_to_repo, \"code\"], capture_output=True))         for mr in mrs:             print(project.name, id, mr.title, mr.source_branch, '=>', mr.target_branch)             print(subprocess.run([\"git\", \"checkout\", mr.source_branch], cwd='code', capture_output=True))             print(subprocess.run([\"git\", \"pull\"], cwd='code', capture_output=True))             print(subprocess.run([\"git\", \"commit\", \"--allow-empty\", \"-m\", \"Nothing here\"], cwd='code', capture_output=True))             print(subprocess.run([\"git\", \"push\"], cwd='code', capture_output=True))         shutil.rmtree('code',ignore_errors=True)<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u0441\u043a\u0440\u0438\u043f\u0442\u0430 \u0432\u0441\u0451 \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u0430\u043b\u043e \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/p>\n<p>\u041f\u043e\u043b\u043d\u044b\u0435 \u043b\u0438\u0441\u0442\u0438\u043d\u0433\u0438 Python-\u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432, \u043f\u0440\u0438\u0432\u0435\u0434\u0451\u043d\u043d\u044b\u0445 \u0432 \u0441\u0442\u0430\u0442\u044c\u0435, \u0434\u043e\u0441\u0442\u0443\u043f\u043d\u044b \u0432 <a href=\"https:\/\/github.com\/flant\/examples\/tree\/master\/2021\/09-gitea-gitlab-migration\"><u>\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 flant\/examples<\/u><\/a>.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7c2\/1fc\/176\/7c21fc17664a3e7f037543654a2841f4.png\" alt=\"\u041f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0430\u044f\u0441\u044f \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u044f GitLab. \u0412 \u043d\u0435\u0451 \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u044b\" title=\"\u041f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0430\u044f\u0441\u044f \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u044f GitLab. \u0412 \u043d\u0435\u0451 \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u044b\" width=\"949\" height=\"165\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7c2\/1fc\/176\/7c21fc17664a3e7f037543654a2841f4.png\"\/><figcaption>\u041f\u043e\u043b\u0443\u0447\u0438\u0432\u0448\u0430\u044f\u0441\u044f \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u044f GitLab. \u0412 \u043d\u0435\u0451 \u0431\u044b\u043b\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u044b \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438, \u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0441\u0442\u0430\u0440\u044b\u0435 \u043f\u0440\u043e\u0435\u043a\u0442\u044b \u0443\u0434\u0430\u043b\u0435\u043d\u044b<\/figcaption><\/figure>\n<h2>\u0412\u044b\u0432\u043e\u0434\u044b<\/h2>\n<p>\u041c\u0438\u0433\u0440\u0430\u0446\u0438\u044f \u0438\u0437 Gitea \u0432 GitLab, \u043d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u043a\u0430\u0436\u0443\u0449\u0443\u044e\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0443, \u043e\u043a\u0430\u0437\u0430\u043b\u0430\u0441\u044c \u043d\u0435\u043f\u0440\u043e\u0441\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0435\u0439. \u0427\u0442\u043e\u0431\u044b \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430, \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0440\u044f\u0434 \u0441\u043a\u0440\u0438\u043f\u0442\u043e\u0432 \u0438 \u043f\u0440\u043e\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043d\u0435\u043e\u0436\u0438\u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u044e\u0430\u043d\u0441\u043e\u0432 \u0438\u0437-\u0437\u0430 \u043d\u0435\u043f\u043e\u043b\u043d\u043e\u0442\u044b \u0432 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0441\u043e\u0432\u043c\u0435\u0441\u0442\u0438\u043c\u043e\u0441\u0442\u0438 \u0440\u0430\u0437\u043d\u044b\u0445 API. \u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u044d\u0442\u043e \u0443\u0434\u0430\u043b\u043e\u0441\u044c, \u0438 \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0447\u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u043e\u043f\u044b\u0442 \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u043a\u043e\u043c\u0443-\u043d\u0438\u0431\u0443\u0434\u044c \u0432 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u0438 \u043e\u0431\u043b\u0435\u0433\u0447\u0438\u0442 \u0436\u0438\u0437\u043d\u044c.<\/p>\n<h3>P.S.<\/h3>\n<p>\u0427\u0438\u0442\u0430\u0439\u0442\u0435 \u0442\u0430\u043a\u0436\u0435 \u0432 \u043d\u0430\u0448\u0435\u043c \u0431\u043b\u043e\u0433\u0435:<\/p>\n<ul>\n<li>\n<p>\u00ab<a href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/491888\/\">GitLab CI: 6 \u0444\u0438\u0447 \u0438\u0437 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0445 \u0440\u0435\u043b\u0438\u0437\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u043c\u044b \u0442\u0430\u043a \u0436\u0434\u0430\u043b\u0438<\/a>\u00bb;<\/p>\n<\/li>\n<li>\n<p>\u00ab<a href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/478690\/\">\u0414\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0441\u0431\u043e\u0440\u043a\u0430 \u0438 \u0434\u0435\u043f\u043b\u043e\u0439 Docker-\u043e\u0431\u0440\u0430\u0437\u043e\u0432 \u0441 werf \u043d\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0441\u0430\u0439\u0442\u0430 \u0432\u0435\u0440\u0441\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u043e\u0439 \u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0430\u0446\u0438\u0438<\/a>\u00bb;<\/p>\n<\/li>\n<li>\n<p> \u00ab<a href=\"https:\/\/habr.com\/ru\/company\/flant\/blog\/469541\/\">\u0421\u0431\u043e\u0440\u043a\u0430 \u0438 \u0434\u0435\u043f\u043b\u043e\u0439 \u043e\u0434\u043d\u043e\u0442\u0438\u043f\u043d\u044b\u0445 \u043c\u0438\u043a\u0440\u043e\u0441\u0435\u0440\u0432\u0438\u0441\u043e\u0432 \u0441 werf \u0438 GitLab CI<\/a>\u00bb.<\/p>\n<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/577808\/\"> https:\/\/habr.com\/ru\/articles\/577808\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u043c\u0438\u0440\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0441\u0438\u0441\u0442\u0435\u043c \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043a\u043e\u0434\u0430. \u0420\u0430\u0437\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u043e\u043d\u0438 \u043a\u0430\u043a \u043f\u0440\u043e\u0442\u043e\u043a\u043e\u043b\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b: Git, Mercurial, Bazaar, \u2014 \u0442\u0430\u043a \u0438 \u0444\u043e\u0440\u043c\u0430\u0442\u043e\u043c \u0440\u0430\u0431\u043e\u0442\u044b (cloud, self-hosted). \u041d\u043e \u0435\u0441\u0442\u044c \u0438 \u0434\u0440\u0443\u0433\u043e\u0439 \u0432\u0430\u0436\u043d\u044b\u0439 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440 \u043f\u0440\u0438 \u0438\u0445 \u0432\u044b\u0431\u043e\u0440\u0435: \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u0438\u043d\u0442\u0435\u0433\u0440\u0430\u0446\u0438\u0438 \u0441 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0435\u043c: issue tracker, CI\/CD, wiki \u0438 \u0442.\u0434. \u0422\u0430\u043a \u0441\u043b\u043e\u0436\u0438\u043b\u043e\u0441\u044c, \u0447\u0442\u043e \u043c\u044b \u0432 \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 \u043f\u0440\u0435\u0434\u043f\u043e\u0447\u0438\u0442\u0430\u0435\u043c GitLab (\u0432\u0430\u0440\u0438\u0430\u043d\u0442 on-premise) \u0438 \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e, \u0435\u0441\u043b\u0438 \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u043f\u0440\u043e\u0442\u0438\u0432, \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u0435\u043c \u0435\u043c\u0443 \u044d\u0442\u043e \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0443 \u043f\u0440\u043e \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u044e \u0438\u0437 Gitea c Jenkins \u0432 GitLab \u0438 \u043e \u0442\u043e\u043c, \u0441 \u043a\u0430\u043a\u0438\u043c\u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0441\u0442\u044f\u043c\u0438 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u0442\u044c\u0441\u044f, \u0430 \u0437\u0430\u043e\u0434\u043d\u043e \u043f\u043e\u0434\u0435\u043b\u044e\u0441\u044c Python-\u0441\u043a\u0440\u0438\u043f\u0442\u0430\u043c\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0433\u043e\u0434\u0438\u043b\u0438\u0441\u044c \u0434\u043b\u044f \u0443\u0441\u043f\u0435\u0445\u0430 \u044d\u0442\u043e\u0433\u043e \u043c\u0435\u0440\u043e\u043f\u0440\u0438\u044f\u0442\u0438\u044f.<\/p>\n<p><strong><em>\u0412\u0430\u0436\u043d\u043e!<\/em><\/strong><em> \u0412 \u0441\u0442\u0430\u0442\u044c\u0435 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0435\u0442\u0441\u044f Gitea 1.13.4 \u0438 GitLab 13.8. \u0412 \u043d\u043e\u0432\u044b\u0445 \u0432\u0435\u0440\u0441\u0438\u044f\u0445 \u043c\u043e\u0433\u0443\u0442 \u0431\u044b\u0442\u044c \u043a\u0430\u043a\u0438\u0435-\u0442\u043e \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043e\u0431\u043b\u0435\u0433\u0447\u0430\u0442 \u043f\u0435\u0440\u0435\u043d\u043e\u0441, \u043d\u043e \u043d\u0430 \u043c\u043e\u043c\u0435\u043d\u0442 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438 \u044d\u0442\u0438 \u0432\u0435\u0440\u0441\u0438\u0438 \u0431\u044b\u043b\u0438 \u0430\u043a\u0442\u0443\u0430\u043b\u044c\u043d\u044b\u043c\u0438.<\/em><\/p>\n<h2>\u041d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e Gitea \u0438 \u0437\u0430\u0434\u0430\u0447\u0443<\/h2>\n<p><a href=\"https:\/\/gitea.io\/en-us\/\"><u>Gitea<\/u><\/a> \u2014 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u0430\u044f Open Source-\u0441\u0438\u0441\u0442\u0435\u043c\u0430 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f Git-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c\u0438. \u042d\u0442\u043e \u0444\u043e\u0440\u043a \u0434\u0440\u0443\u0433\u043e\u0439 \u043b\u0435\u0433\u043a\u043e\u0432\u0435\u0441\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u2014 <a href=\"https:\/\/github.com\/gogs\/gogs\"><u>Gogs<\/u><\/a>. \u041e\u043d\u0430 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c\u044e \u0441\u043e\u0447\u0435\u0442\u0430\u0442\u044c \u0432 \u043e\u0434\u043d\u043e\u043c \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0435 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0441 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u043e \u0448\u0438\u0440\u043e\u043a\u0438\u043c \u0441\u043f\u0435\u043a\u0442\u0440\u043e\u043c \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043a \u043f\u0440\u0430\u0432 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 GitHub-\u043f\u043e\u0434\u043e\u0431\u043d\u044b\u043c API.<\/p>\n<p>\u041f\u0440\u043e\u0435\u043a\u0442 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 25 \u0442\u044b\u0441\u044f\u0447 \u0437\u0432\u0451\u0437\u0434 \u043d\u0430 <a href=\"https:\/\/github.com\/go-gitea\/gitea\"><u>GitHub<\/u><\/a>. \u0421\u0440\u0435\u0434\u0438 \u0441\u043f\u043e\u043d\u0441\u043e\u0440\u043e\u0432 \u2014 DigitalOcean \u0438 Equinix, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u0430\u0442\u044c Gitea \u043c\u043e\u0436\u043d\u043e \u043d\u0430 <a href=\"https:\/\/opencollective.com\/gitea\"><u>Open Collective<\/u><\/a> <em>(\u0438 \u043f\u0440\u043e\u0441\u043b\u0435\u0434\u0438\u0442\u044c, \u043a\u0443\u0434\u0430 \u043f\u043e\u0439\u0434\u0443\u0442 \u0432\u0430\u0448\u0438 \u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0430)<\/em>.\u00a0<\/p>\n<figure class=\"full-width\"><figcaption>\u041a\u0430\u043a \u0432\u044b\u043b\u044f\u0434\u0438\u0442 \u0432\u0435\u0431-\u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441 Gitea<\/figcaption><\/figure>\n<p>\u0418\u0437 \u043f\u043b\u044e\u0441\u043e\u0432 \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e Gitea \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430 \u043a\u0430\u043a \u0432 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0435 \u0438 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0435, \u0442\u0430\u043a \u0438 \u0432 \u0431\u044d\u043a\u0430\u043f\u0435. \u0421\u0438\u0441\u0442\u0435\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043d\u0430 \u043b\u044e\u0431\u043e\u043c \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u043c \u043a\u043e\u043c\u043f\u044c\u044e\u0442\u0435\u0440\u0435, \u0438 \u043e\u043d\u0430 \u043f\u0440\u0435\u0434\u043b\u043e\u0436\u0438\u0442 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0432\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0435 \u0441\u0435\u0440\u0432\u0438\u0441\u044b: wiki-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b, \u0437\u0430\u0434\u0430\u0447\u0438 \u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u044b, \u0442.\u0435. todo-\u043b\u0438\u0441\u0442\u044b.<\/p>\n<p>\u041d\u043e \u044d\u0442\u0430 \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0430 \u043c\u043e\u0436\u0435\u0442 \u0438\u043c\u0435\u0442\u044c \u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u0443\u044e \u0441\u0442\u043e\u0440\u043e\u043d\u0443. \u0412 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u043d\u0435\u0442 \u0433\u043e\u0442\u043e\u0432\u043e\u0433\u043e CI\/CD, \u0438 \u0434\u043b\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u044d\u0442\u0438\u0445 \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u0432 \u043f\u0440\u0438\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0435\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435. \u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0443 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u044d\u0442\u0443 \u0440\u043e\u043b\u044c \u0438\u0433\u0440\u0430\u043b Jenkins, \u0434\u043b\u044f \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u0435\u0442 <a href=\"https:\/\/github.com\/jenkinsci\/gitea-plugin\"><u>\u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043f\u043b\u0430\u0433\u0438\u043d<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u0434\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0431\u043e\u0440 \u0431\u044b\u043b \u0441\u043a\u043e\u0440\u0435\u0435 \u0438\u0441\u0442\u043e\u0440\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u043d\u0430\u0441\u043b\u0435\u0434\u0438\u0435\u043c, \u0447\u0435\u043c \u0442\u0435\u0445\u043d\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u044c\u044e. CI\/CD \u0441 \u043d\u0438\u043c \u0431\u044b\u043b \u043d\u0435 \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u0435\u043d \u0432 \u0440\u0430\u0431\u043e\u0442\u0435. \u0414\u043b\u044f \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430 \u0434\u0435\u043f\u043b\u043e\u044f \u043c\u044b \u0441\u043e\u0448\u043b\u0438\u0441\u044c \u043d\u0430 \u043f\u0435\u0440\u0435\u0445\u043e\u0434\u0435 \u043d\u0430 GitLab, \u0430 \u044d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u043b\u043e \u0438 \u0437\u0430\u043c\u0435\u043d\u0443 \u0441\u0430\u043c\u043e\u0439 Gitea, \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0442\u0435\u0440\u044f\u043b\u0438 \u0441\u043c\u044b\u0441\u043b. \u0422\u0430\u043a\u0436\u0435 \u0432 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0431\u043e\u0442 \u0431\u044b\u043b\u0438 \u043d\u0430\u0439\u0434\u0435\u043d\u044b \u043c\u0435\u043b\u043a\u0438\u0435 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u0435\u0448\u0430\u043b\u0438 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438.<\/p>\n<h3>\u0427\u0442\u043e \u0431\u044b\u043b\u043e \u043d\u0430 \u0441\u0442\u0430\u0440\u0442\u0435<\/h3>\n<p>\u0418\u0441\u0445\u043e\u0434\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u2014 Gitea 1.13.4 \u0441\u043e 165 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u043c\u0438 \u0438 94 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c\u0438.\u00a0\u0412\u0441\u0451 \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u0440\u0430\u0437\u043b\u043e\u0436\u0435\u043d\u043e \u043f\u043e 18 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u044f\u043c\u0438, \u0430 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 \u0431\u044b\u043b\u0438 \u043b\u0438\u0447\u043d\u044b\u043c\u0438.<\/p>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043a\u043b\u0438\u0435\u043d\u0442 \u043d\u0435 \u0445\u043e\u0442\u0435\u043b \u0442\u0435\u0440\u044f\u0442\u044c \u0438\u0441\u0442\u043e\u0440\u0438\u044e pull requests, \u0430 \u0438\u0445 \u0431\u044b\u043b\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043c\u043d\u043e\u0433\u043e: \u0432 \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f\u0445 \u2014 \u0431\u043e\u043b\u0435\u0435 5 \u0442\u044b\u0441\u044f\u0447.<\/p>\n<figure class=\"full-width\"><figcaption>\u0421\u0442\u0430\u0442\u0438\u0441\u0442\u0438\u043a\u0430 \u043f\u043e \u0438\u043d\u0441\u0442\u0430\u043b\u043b\u044f\u0446\u0438\u0438 Gitea \u0434\u043e \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430<\/figcaption><\/figure>\n<p>\u041d\u0435\u0441\u043c\u043e\u0442\u0440\u044f \u043d\u0430 \u0442\u043e, \u0447\u0442\u043e \u0441\u0430\u043c\u0438\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u0430 \u0442\u0430\u043a\u0436\u0435 \u043e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0439 \u0438 \u0433\u0440\u0443\u043f\u043f \u0431\u044b\u043b\u043e \u043d\u0435 \u0442\u0430\u043a \u0443\u0436 \u0438 \u043c\u043d\u043e\u0433\u043e, \u00ab\u043d\u0430\u043a\u043b\u0438\u043a\u0438\u0432\u0430\u0442\u044c\u00bb \u0442\u0430\u043a\u043e\u0435 (\u0434\u0430 \u0435\u0449\u0451 \u0438 \u0431\u0435\u0437 \u043e\u0448\u0438\u0431\u043e\u043a) \u2014 \u044d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u0434\u043e\u043b\u0433\u043e.\u00a0\u00a0<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<p>\u0414\u0430 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435, \u043c\u044b \u0436\u0435 \u0438\u043d\u0436\u0435\u043d\u0435\u0440\u044b! \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0448\u043b\u0438 \u043f\u0443\u0442\u0435\u043c \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u043d\u0438\u0436\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u043c \u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430\u0445, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u043f\u0440\u0435\u043e\u0434\u043e\u043b\u0435\u0442\u044c. \u041f\u043e\u043f\u0443\u0442\u043d\u043e \u043c\u044b \u043d\u0430\u0443\u0447\u0438\u043c\u0441\u044f \u0440\u0430\u0431\u043e\u0442\u0435 \u0441 REST API \u043e\u0431\u043e\u0438\u0445 \u0440\u0435\u0448\u0435\u043d\u0438\u0439: \u0438 Gitea, \u0438 GitLab. \u041f\u043e\u0441\u0435\u043c\u0443 \u044d\u0442\u043e\u0442 \u043e\u043f\u044b\u0442 \u043c\u043e\u0436\u0435\u0442 \u043e\u043a\u0430\u0437\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u043b\u0435\u0437\u043d\u044b\u043c \u043d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043b\u044f \u043d\u0435\u043f\u043e\u0441\u0440\u0435\u0434\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043d\u043e \u0438 \u043f\u043e\u0432\u0441\u0435\u0434\u043d\u0435\u0432\u043d\u044b\u0445 \u0437\u0430\u0434\u0430\u0447.<\/p>\n<h2>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f: \u0438\u043c\u043f\u043e\u0440\u0442, \u044d\u043a\u0441\u043f\u043e\u0440\u0442, \u043f\u0435\u0440\u0435\u043d\u043e\u0441<\/h2>\n<p>\u0418\u0442\u0430\u043a, \u043f\u0435\u0440\u0435\u0439\u0434\u0435\u043c \u043a \u0441\u0430\u043c\u043e\u0439 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438. \u041e\u0431\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0438\u043c\u0435\u044e\u0442 \u0440\u0430\u0437\u0432\u0438\u0442\u044b\u0439 <a href=\"https:\/\/docs.gitea.io\/en-us\/api-usage\/\"><u>API<\/u><\/a>. \u0412\u044b\u0431\u0435\u0440\u0435\u043c \u043a\u043b\u0438\u0435\u043d\u0442\u044b \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 API \u043e\u0431\u0435\u0438\u0445 \u0441\u0438\u0441\u0442\u0435\u043c:<\/p>\n<ul>\n<li>\n<p>\u0414\u043b\u044f Gitea \u044f \u0432\u044b\u0431\u0440\u0430\u043b Python-\u0432\u0430\u0440\u0438\u0430\u043d\u0442 <a href=\"https:\/\/github.com\/dblueai\/giteapy\"><u>giteapy<\/u><\/a>. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u043e\u043d \u043d\u0435 \u0442\u0430\u043a \u0445\u043e\u0440\u043e\u0448, \u043a\u0430\u043a \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u044b\u0439 \u0434\u043b\u044f GitLab. <em>(\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u043f\u043e\u0432\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u0435\u0449\u0451 \u0432\u0441\u0442\u0440\u0435\u0442\u044f\u0442\u0441\u044f \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0440\u0435\u043c\u0430\u0440\u043a\u0438.)<\/em><\/p>\n<\/li>\n<li>\n<p>\u0414\u043b\u044f GitLab \u2014 <a href=\"https:\/\/github.com\/python-gitlab\/python-gitlab\"><u>python-gitlab<\/u><\/a>.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0427\u0430\u0441\u0442\u044c 1. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438<\/h3>\n<p>\u0414\u043b\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u043a GitLab \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0435\u0434\u0438\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441:<\/p>\n<pre><code class=\"python\">import gitlab  gl = gitlab.Gitlab('https:\/\/gitlab.example.com', private_token='secret')  gl_users = gl.users.list(page=1, per_page=1000)<\/code><\/pre>\n<p>\u0412 giteapy \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0440\u0430\u0437\u0434\u0435\u043b\u0430 API \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u043e\u0434\u043a\u043b\u0430\u0441\u0441. \u0418\u0445 \u043e\u0431\u0449\u0435\u0435 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u2014 6, \u043d\u043e \u043d\u0430\u043c \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e 4: <code>AdminApi<\/code>, <code>OrganizationApi<\/code>, <code>RepositoryApi<\/code>, <code>UserApi<\/code>. \u0412\u043e\u0442 \u043a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u043a\u043e\u0434 <em>(\u043f\u043e\u043b\u043d\u044b\u0435 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0435 \u043b\u0438\u0441\u0442\u0438\u043d\u0433\u0438 \u2014 \u0441\u043c. \u0432 <\/em><a href=\"https:\/\/github.com\/flant\/examples\/tree\/master\/2021\/09-gitea-gitlab-migration\"><em><u>\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438 flant\/examples<\/u><\/em><\/a><em>)<\/em>:<\/p>\n<pre><code class=\"python\">import giteapy configuration = giteapy.Configuration() configuration.api_key['access_token'] = 'secret' configuration.host = 'https:\/\/git.example.com\/api\/v1'  admin_api = giteapy.AdminApi(giteapy.ApiClient(configuration)) user_api_instance = giteapy.UserApi(giteapy.ApiClient(configuration)) org_api_instance = giteapy.OrganizationApi(giteapy.ApiClient(configuration)) repo_api_instance = giteapy.RepositoryApi(giteapy.ApiClient(configuration))  gt_users = admin_api_instance.admin_get_all_users()<\/code><\/pre>\n<p>\u0418 \u0443\u0436\u0435 \u0442\u0443\u0442 \u043e\u0436\u0438\u0434\u0430\u043b \u043f\u0435\u0440\u0432\u044b\u0439 \u0441\u044e\u0440\u043f\u0440\u0438\u0437:<\/p>\n<pre><code class=\"python\">{'avatar_url': 'https:\/\/gitea.example.com\/user\/avatar\/user1\/-1',  'created': datetime.datetime(2018, 10, 11, 19, 0, 0, tzinfo=tzutc()),  'email': 'user1@example.com',  'full_name': 'User Name',  'id': 2,  'is_admin': False,  'language': 'ru-RU',  'last_login': datetime.datetime(2020, 10, 19, 8, 0, 0, tzinfo=tzutc()),  'login': 'user1'}<\/code><\/pre>\n<p>\u0412\u044b \u0441\u043f\u0440\u043e\u0441\u0438\u0442\u0435: \u0447\u0442\u043e \u0436\u0435 \u043d\u0435 \u0442\u0430\u043a? \u041e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e: \u0432 \u0432\u044b\u0432\u043e\u0434\u0435 API \u043d\u0435 \u0432\u0438\u0434\u043d\u043e, <strong>\u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438\u043b\u0438 \u043d\u0435\u0442<\/strong>. \u0418\u0437\u0443\u0447\u0435\u043d\u0438\u0435 \u0432\u0441\u0435\u0445 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043f\u0440\u0438\u0432\u0435\u043b\u043e \u043a \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0441\u0442\u0438 \u0437\u0430\u043f\u0440\u0430\u0448\u0438\u0432\u0430\u0442\u044c \u044d\u0442\u0443 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e \u0438\u0437 \u0431\u0430\u0437\u044b Gitea. \u0411\u043b\u0430\u0433\u043e, \u044d\u0442\u043e \u043d\u0435 \u0442\u0430\u043a\u0430\u044f \u0443\u0436 \u0438 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u2014 \u043e\u043d\u0430 \u0440\u0435\u0448\u0430\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u0441\u0442\u044b\u043c \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u043c:<\/p>\n<pre><code class=\"python\">SELECT is_active FROM \u201cuser\u201d WHERE id = &lt;user_id><\/code><\/pre>\n<p>\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u0431\u044b\u043b\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e (\u043e\u043a\u043e\u043b\u043e 10), \u0438 \u043c\u044b \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u043b\u0438 \u0438\u0445 \u0432\u0440\u0443\u0447\u043d\u0443\u044e.<\/p>\n<h4>SSH-\u043a\u043b\u044e\u0447\u0438<\/h4>\n<p>\u041b\u043e\u0433\u0438\u0447\u043d\u043e \u043f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u0442\u044c, \u0447\u0442\u043e \u0435\u0441\u043b\u0438 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0434\u0430\u0447\u0430 \u043c\u0438\u0433\u0440\u0430\u0446\u0438\u0438, \u043d\u0430\u043c \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0442\u0438 \u0435\u0449\u0435 \u0438 SSH-\u043a\u043b\u044e\u0447\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 API Gitea \u043e\u043f\u0438\u0441\u0430\u043d \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/dblueai\/giteapy\/blob\/master\/docs\/UserApi.md#user_current_get_key\"><u>user_current_get_key<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u0441\u0442\u0440\u0430\u043d\u043d\u044b\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<ul>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043c\u043d\u043e\u0433\u043e \u043a\u043b\u044e\u0447\u0435\u0439, \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u0432\u0441\u0435\u0433\u043e \u043e\u0434\u0438\u043d \u043a\u043b\u044e\u0447;<\/p>\n<\/li>\n<li>\n<p>\u0435\u0441\u043b\u0438 \u043a\u043b\u044e\u0447\u0435\u0439 \u043d\u0435\u0442 \u2014 \u043e\u043d \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043e\u0448\u0438\u0431\u043a\u0443 404.<\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u0440\u0438 \u043f\u0435\u0440\u0432\u0438\u0447\u043d\u043e\u043c \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0435 \u043c\u044b \u044d\u0442\u043e \u043e\u0431\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u0441\u0442\u0432\u043e \u043d\u0435 \u0443\u0447\u043b\u0438 \u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0432\u044b\u0437\u043e\u0432 API \u043a\u0430\u043a \u0435\u0441\u0442\u044c. \u0412 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0435 \u0431\u044b\u043b\u0438 \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u044b \u043d\u0435\u0432\u0435\u0440\u043d\u044b\u0435 \u043a\u043b\u044e\u0447\u0438 \u2014 \u0438\u0445 \u043d\u0435 \u0445\u0432\u0430\u0442\u0430\u043b\u043e. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043d\u0430\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u043e\u0432\u0435\u0442\u0443\u044e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <a href=\"https:\/\/github.com\/dblueai\/giteapy\/blob\/master\/docs\/UserApi.md#user_list_keys\"><u>user_list_keys<\/u><\/a>. \u041e\u0434\u043d\u0430\u043a\u043e \u0438 \u0441 \u043d\u0438\u043c \u043d\u0430\u0441 \u0436\u0434\u0430\u043b \u043f\u043e\u0434\u0432\u043e\u0445: \u0432 \u0431\u0430\u0437\u0435 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0443\u0435\u0442 <em>\u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439<\/em> \u0438\u043d\u0434\u0435\u043a\u0441 \u043f\u043e fingerprint.<\/p>\n<pre><code class=\"python\">Indexes:     \"public_key_pkey\" PRIMARY KEY, btree (id)     \"IDX_public_key_fingerprint\" btree (fingerprint)     \"IDX_public_key_owner_id\" btree (owner_id)<\/code><\/pre>\n<p>\u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u0432 \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u0435\u0440\u0435\u043d\u043e\u0441\u0430 \u043f\u0440\u0438\u0448\u043b\u043e\u0441\u044c \u0440\u0435\u0448\u0430\u0442\u044c \u043a\u043e\u043d\u0444\u043b\u0438\u043a\u0442\u043d\u044b\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438 \u043f\u0440\u0438 \u0438\u043c\u043f\u043e\u0440\u0442\u0435 \u043a\u043b\u044e\u0447\u0435\u0439 \u0432 GitLab. \u041a \u0441\u0447\u0430\u0441\u0442\u044c\u044e, \u044d\u0442\u0438 \u043a\u043b\u044e\u0447\u0438 \u0431\u044b\u043b\u0438 \u0432 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u043c \u0443 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u043f\u0440\u0438\u043d\u044f\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e\u0435 \u0440\u0435\u0448\u0435\u043d\u0438\u0435 \u2014 \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0432\u0441\u0435 \u043a\u043b\u044e\u0447\u0438 \u0443 \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u044b\u043b\u0438 \u043f\u0435\u0440\u0435\u043d\u0435\u0441\u0435\u043d\u044b. \u0412 GitLab \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\"># clean blocked users keys for block_gl_user in gl.users.list(blocked=True, page=1, per_page=10000):     print(\"Blocker user\", block_gl_user.username)     for block_gl_user_key in block_gl_user.keys.list():         print(\"Found key\", block_gl_user_key.title)         block_gl_user_key.delete()<\/code><\/pre>\n<h4>\u041f\u0440\u0430\u0432\u0430<\/h4>\n<p>\u0421\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u0448\u0430\u0433 \u2014 \u0432\u044b\u0434\u0430\u0447\u0430 \u0432\u0435\u0440\u043d\u044b\u0445 \u043f\u0440\u0430\u0432. <em>\u041e\u0440\u0433\u0430\u043d\u0438\u0437\u0430\u0446\u0438\u0438<\/em> Gitea \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 <em>\u0433\u0440\u0443\u043f\u043f\u044b<\/em> GitLab, \u0430 <em>\u043a\u043e\u043c\u0430\u043d\u0434\u044b<\/em> \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u044e\u0442\u0441\u044f \u0432 <em>\u043f\u0440\u0430\u0432\u0430 \u0434\u043e\u0441\u0442\u0443\u043f\u043e\u0432<\/em>.<\/p>\n<p>\u041f\u043e\u043b\u0443\u0447\u0438\u0432 \u0432\u0441\u0435 \u043a\u043e\u043c\u0430\u043d\u0434\u044b \u0438\u0437 API, \u043c\u044b \u0441\u043e\u0433\u043b\u0430\u0441\u043e\u0432\u0430\u043b\u0438 \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u043e\u043c <strong>\u043c\u0430\u0442\u0440\u0438\u0446\u0443 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432<\/strong>:<\/p>\n<pre><code class=\"python\"># map access rules map_access = {'Owners': gitlab.OWNER_ACCESS,               'Developers': gitlab.DEVELOPER_ACCESS,               'QA': gitlab.DEVELOPER_ACCESS,               'Manager':gitlab.REPORTER_ACCESS,               'Managers': gitlab.REPORTER_ACCESS,               'Dev': gitlab.DEVELOPER_ACCESS,               'Services': gitlab.REPORTER_ACCESS,               'services': gitlab.REPORTER_ACCESS}  # inspect Gitea orgs and create Gitlab groups # get all orgs gt_all_orgs = admin_api_instance.admin_get_all_orgs() for gt_org in gt_all_orgs:     # does the group exist?     res = None     try:         res = gl.groups.get(gt_org.username)     except:         pass      if res:         # append existing groups to dictionary          dict_gl_groups[gt_org.username] = res     else:         # create the missing group         gl_group = gl.groups.create({'name': gt_org.username, 'path': gt_org.username})         if len(gt_org.description) > 0:             gl_group.description = gt_org.description         if len(gt_org.full_name) > 0:             gl_group.full_name = gt_org.full_name         gl_group.save()         dict_gl_groups[org.username] = gl_group     # list teams for the Gitea org     gt_org_teams = org_api_instance.org_list_teams(gt_org.username)     for team in teams:         # get all team members         members = org_api_instance.org_list_team_members(team.id)         for user in members:             # add members to groups with their access level             # dict_gl_users was created on user creation step             member = dict_gl_groups[gt_org.username].members.create({'user_id': dict_gl_users[user.login].id, 'access_level': map_access.get(team.name, gitlab.REPORTER_ACCESS)})<\/code><\/pre>\n<h4>\u041f\u043e\u0447\u0442\u043e\u0432\u044b\u0435 \u0443\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f<\/h4>\n<p>\u0427\u0442\u043e \u043f\u0440\u043e\u0438\u0437\u043e\u0439\u0434\u0435\u0442 \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u0438\u0445 \u043c\u0430\u043d\u0438\u043f\u0443\u043b\u044f\u0446\u0438\u0439? \u041f\u0435\u0440\u0435\u043d\u043e\u0441 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439 \u00ab\u043f\u043e\u0440\u043e\u0434\u0438\u0442\u00bb \u043c\u0430\u0441\u0441\u0443 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0445 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439: \u043e \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u043e \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0430 \u0438 \u0442.\u043f. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u043e\u0438\u0442 \u0437\u0430\u0440\u0430\u043d\u0435\u0435 \u0440\u0435\u0448\u0438\u0442\u044c, \u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0442\u044c \u044d\u0442\u0443 \u043a\u043e\u0440\u0440\u0435\u0441\u043f\u043e\u043d\u0434\u0435\u043d\u0446\u0438\u044e \u0438\u043b\u0438 \u0436\u0435 \u043f\u0435\u0440\u0435\u043d\u0430\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0432 \u0447\u0451\u0440\u043d\u0443\u044e \u0434\u044b\u0440\u0443.<\/p>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u043d\u0435 \u0445\u043e\u0442\u0438\u0442\u0435 \u0440\u0430\u0441\u0441\u044b\u043b\u0430\u0442\u044c \u043f\u043e\u0447\u0442\u0443 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435 \u043f\u0430\u0440\u043e\u043b\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u043e\u0442\u043e\u043c \u0440\u0430\u0437\u0434\u0430\u0434\u0438\u0442\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c, \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">gl_user = gl.users.create({'email': gt_user.email,                                    'password': password,                                    'username': gt_user.login,                                    'name': gt_user.full_name if len(gt_user.full_name) > 0 else gt_user.login,                                    'admin': gt_user.is_admin,                                    'skip_confirmation': True})<\/code><\/pre>\n<p>\u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0435\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0432\u0441\u0435 \u043f\u0438\u0441\u044c\u043c\u0430 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0432 <code>\/dev\/null<\/code>. \u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u0434\u043e\u0439\u0434\u0451\u0442 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 <a href=\"https:\/\/www.vanimpe.eu\/2012\/04\/03\/run-postfix-as-a-blackhole-mail-server-open-relay\/\"><u>\u043a\u043e\u043d\u0444\u0438\u0433 Postfix<\/u><\/a>:<\/p>\n<pre><code class=\"python\">relayhost =  relay_transport = relay relay_domains = static:ALL smtpd_end_of_data_restrictions = check_client_access static:discard<\/code><\/pre>\n<p>\u0412 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043a\u043b\u0438\u0435\u043d\u0442 \u0441\u043d\u0430\u0447\u0430\u043b\u0430 \u043f\u043e\u043f\u0440\u043e\u0441\u0438\u043b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0441 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u043f\u043e\u0447\u0442\u044b \u0432 \u00ab\u0447\u0435\u0440\u043d\u0443\u044e \u0434\u044b\u0440\u0443\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u0432\u0441\u0435 \u043f\u043e\u0447\u0442\u043e\u0432\u044b\u0435 \u0430\u0434\u0440\u0435\u0441\u0430. \u0415\u0441\u043b\u0438 \u043d\u0435 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u0442\u044c \u043f\u0440\u0438 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f <code>skip_confirmation<\/code>, \u0442\u043e \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c, \u0435\u0441\u043b\u0438 \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0434\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0435\u0439, \u044d\u0442\u043e \u043d\u0430\u0434\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0432\u0440\u0443\u0447\u043d\u0443\u044e. \u041a \u0441\u043e\u0436\u0430\u043b\u0435\u043d\u0438\u044e, \u044d\u0442\u043e <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab\/-\/issues\/212539#:~:text=go%20to%20the%20Admin%20Area,block%20will%20be%20still%20shown.\"><u>\u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u0431\u0430\u0433<\/u><\/a> GitLab: \u0434\u043b\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u044f <a href=\"https:\/\/gitlab.com\/gitlab-org\/gitlab\/-\/issues\/212539#note_323746089\"><u>\u043f\u0440\u0438\u0434\u0435\u0442\u0441\u044f<\/u><\/a> \u043b\u0435\u0437\u0442\u044c \u0432 \u043a\u043e\u043d\u0441\u043e\u043b\u044c Rails.<\/p>\n<h4>\u041f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u0435 \u0438\u0442\u043e\u0433\u0438<\/h4>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u044b, \u0432\u044b\u044f\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0432 Gitea \u0437\u0430 \u0432\u0440\u0435\u043c\u044f \u044d\u0442\u0438\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439:<\/p>\n<ol>\n<li>\n<p>\u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0435 \u0432\u0430\u0436\u043d\u043e\u0439 \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u0438 \u043e \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430\u0445 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 API;<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435\u043e\u0447\u0435\u0432\u0438\u0434\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b API \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u043b\u044e\u0447\u0430\u043c\u0438;<\/p>\n<\/li>\n<li>\n<p>\u0434\u0443\u0431<\/p>\n<\/li>\n<\/ol>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-396279","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/396279","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=396279"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/396279\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=396279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=396279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=396279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}