Дата и время публикации:
Проблема и решение
1. Cуть проблемы
Допустим, что вы разработчик и работает над собственными проектами, некоторые из которых находятся в совместной разработке по всему миру и находятся на GitHub, или других крупных системах контроля версий (versioning system). Однако, некоторые проекты могут быть настолько маленькими и закрытыми для публичного доступа, что размещение на публичных репозитариях является не желательным, потому что они могут быть пропроетарными или предназначенными только для внутреннего использования.[3.1]
Тем не менее, такая потребность есть всегда, поэтому для примера возьмем два проекта: первый — публичный, размещенный на GitHub проект procps-ptree [3.2], а второй – распространяемый в виде TAR-болла с тестовым примером для Flawfinder программы на С [3.3], оформленный мной в виде файла example-1.1.tar.gz, содержащий проект исходного кода, собираемый автоматически с помощью GNU Automake [3.4].
2. Решение
2.1 Удаленный репозитарий (remote repo)
На локальном узле создадим пользователя USER=git, вместе с назначением ему домашней директории /home/$USER и командной оболочки git-shell, как показано в дампе 2.1.1
Дамп 2.2.1
user@debian-11uni: ~/example-1.1$ git init -b v1.1 Initialized empty Git repository in /home/user/Build/yocto-poky/git-server/example-1.1/.git/ user@debian-11uni: ~/example-1.1$ git remote add local_proj /home/git/repos/example.git user@debian-11uni: ~/example-1.1$ git add README.md user@debian-11uni: ~/example-1.1$ git commit -m 'Store the file README.md' [v1.1 (root-commit) ef6f2c6] Store the file README.md 1 file changed, 10 insertions(+) create mode 100644 README.md user@debian-11uni: ~/example-1.1$ git push --set-upstream origin v1.1 fatal: '/git/repos/example.git' does not appear to be a git repository fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists.
Для исправления ошибки "fatal: '/git/repos/example.git' does not appear to be a git repository" нужно выполнить действия по созданию локальной директории /home/git/repos/example.git и проинициализировать пустой с веткой master, как показано в дампе 2.2.2
Дамп 2.2.2
user@debian-11uni: ~/example-1.1$ sudo -u mkdir -m 755 -p /home/git/repos/example.git && cd /home/git/repos/example.git user@debian-11uni:/home/git/repos/example.git$ sudo -u git git init . Initialized empty Git repository in /home/git/repos/example.git/.git/ user@debian-11uni:/home/git/repos/example.git$ sudo -u git git config receive.denyCurrentBranch ignore
После чего, можно повторить git-push(1) в точке положения исходного кода ~/example-1.1, как было показано ранее в 2.2.1, но с некоторыми ньюансами [3.7], которые показаны в дампе 2.2.3
Дамп 2.2.3
user@debian-11uni: ~/$ git clone /home/git/repos/example.git && cd example user@debian-11uni: ~/example$ cp ../example-1.1/README.md . user@debian-11uni: ~/example$ git add README.md user@debian-11uni: ~/example$ git commit -m 'Store the file README.md' $ git push --set-upstream origin master Перечисление объектов: 3, готово. Подсчет объектов: 100% (3/3), готово. При сжатии изменений используется до 8 потоков Сжатие объектов: 100% (2/2), готово. Запись объектов: 100% (3/3), 469 байтов | 469.00 КиБ/с, готово. Всего 3 (изменений 0), повторно использовано 0 (изменений 0), повторно использовано пакетов 0 error: сбой при внешней распаковке unable to create temporary object directory To /home/git/repos/example.git ! [remote rejected] master -> master (unpacker error) error: не удалось отправить некоторые ссылки в «/home/git/repos/example.git»
Для парирования "unable to create temporary object directory, для удаленной директории /home/git/repos/example.git/ выполним команду: sudo -u git chmod a=rwx -r. После выполнения которой, файл README.md будет успешно отправлен в репозитарий удаленной директории /home/git/repos/example.git, как показано в дампе 2.2.4
Дамп 2.2.4
user@debian-11uni: ~/example$ git push --set-upstream origin master Перечисление объектов: 3, готово. Подсчет объектов: 100% (3/3), готово. При сжатии изменений используется до 8 потоков Сжатие объектов: 100% (2/2), готово. Запись объектов: 100% (3/3), 469 байтов | 469.00 КиБ/с, готово. Всего 3 (изменений 0), повторно использовано 0 (изменений 0), повторно использовано пакетов 0 To /home/git/repos/example.git * [new branch] master -> master Ветка «master» отслеживает внешнюю ветку «master» из «origin».
Отсюда, главный недостаток локального протокола — полный доступ сторонних и поту стронних субъектов. Именно это обстоятельство, заставило меня снять ограничение прав доступа для постронних пользователей коим является пользователь с именем «user» поотношению к "git".
Для проверки, работоспособности репозитария и спустя како-то время добавил оставшиеся файлы проекта example из example-1.1.tar.gz, как показано в дампе 2.2.5
Дамп 2.2.5
user@debian-11uni: ~/example$ git $ git commit -m 'add the rest of the files in the project' [master 1cac386] add the rest of the files in the project 28 files changed, 26500 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.am create mode 100644 Makefile.in create mode 100644 aclocal.m4 create mode 100644 autom4te.cache/output.0 create mode 100644 autom4te.cache/output.1 create mode 100644 autom4te.cache/requests create mode 100644 autom4te.cache/traces.0 create mode 100644 autom4te.cache/traces.1 create mode 100755 compile create mode 100644 config.h create mode 100644 config.h.in create mode 100644 config.h.in~ create mode 100644 config.log create mode 100755 config.status create mode 100755 configure create mode 100644 configure.ac create mode 100755 depcomp create mode 100644 docs/Flawfinder-results.pdf create mode 100755 install-sh create mode 100755 missing create mode 100644 src/.deps/example.Po create mode 100644 src/Makefile create mode 100644 src/Makefile.am create mode 100644 src/Makefile.am~ create mode 100644 src/Makefile.in create mode 100644 src/example.c create mode 100644 stamp-h1 user@home2:~/Build/yocto-poky/git-server/example$ git push --set-up Перечисление объектов: 33, готово. Подсчет объектов: 100% (33/33), готово. При сжатии изменений используется до 8 потоков Сжатие объектов: 100% (29/29), готово. Запись объектов: 100% (32/32), 1.26 MiB | 14.63 MiB/s, готово. Total 32 (delta 6), reused 0 (delta 0), pack-reused 0 To /home/git/repos/example.git a61a50c..1cac386 master -> master Ветка «master» отслеживает внешнюю ветку «master» из «origin».
Для примера, ниже привожу импортирование проекта procps_ptree [3.2] из GitHub в локальный репозитарий, как показано в дампе 2.2.6
Дамп 2.2.6
user@home2:~$ git clone --bare https://github.com/rjaan/procps_ptree.git Клонирование в голый репозиторий «procps_ptree.git»… remote: Enumerating objects: 228, done. remote: Counting objects: 100% (228/228), done. remote: Compressing objects: 100% (206/206), done. remote: Total 228 (delta 127), reused 76 (delta 20), pack-reused 0 Получение объектов: 100% (228/228), 168.54 KiB | 1.64 MiB/s, готово. Определение изменений: 100% (127/127), готово.
Далее создал пустой голый репозитарий /home/git/repos/procps_ptree.git, как показано в дампе 2.2.7
Дамп 2.2.7
user@home2:/home/git/repos$ sudo -u git mkdir -m 777 -p procps_ptree.git && cd procps_ptree.git user@home2:/home/git/repos/$ sudo -u git git init --bare procps_ptree.git user@home2:/home/git/repos$ sudo -u git find ./procps_ptree.git -type d -print | sudo -u git xargs chmod a=wrx user@home2:/home/git/repos$ sudo -u git find ./procps_ptree.git -type f -print | sudo -u git xargs chmod a=wr
После чего, выполнил клонирование ~/procps_ptree.git в локальный репозитарий /home/git/repos/procps_ptree.git с использованием опции "mirror", как показано в дампе 2.2.8
Дамп 2.2.8
user@home2:~/procps_ptree.git$ $ git push --mirror /home/git/repos/procps_ptree.git Перечисление объектов: 228, готово. Подсчет объектов: 100% (228/228), готово. При сжатии изменений используется до 8 потоков Сжатие объектов: 100% (99/99), готово. Запись объектов: 100% (228/228), 168.54 KiB | 15.32 MiB/s, готово. Total 228 (delta 127), reused 228 (delta 127), pack-reused 0 remote: Определение изменений: 100% (127/127), готово. To /home/git/repos/procps_ptree.git * [new branch] coverity_scan -> coverity_scan * [new branch] master -> master
2.3 Протокол HTTP
Подразумевает наличие программы сервера HTTP, с ролью которой легко может справиться програмный модуль http.server бинарного интерпретатора кода Python, пример запуска которого приведен в дампе 2.3.1
Дамп 2.3.1
sudo -u git python3 -m http.server --cgi --bind 127.0.0.1 8080 --directory /home/git/repos/
При этом в указанной директории /home/git-repos/ мне потребовалось создать поддиректорию cgi-bin/, т.к. опция --cgi модуля http.server требует этого [3.8], кроме указанных вместе с ней опций --bind для связывания с портом и адресом или доменным именем) и --directory, чтобы указать корневую директорию /home/git-repos/ — хранилища Git
Поддержку протоколов http:// и https:// обеспечивается программным модулем git-http-backend, вызываемый клиентом git командой http-backend, который является простой программой CGI и предназначенной для обслуживания, обеспечения доступа клиентов Git к хранилищу репозитариев Git
Самый простой способ, чтобы посмотреть что за диковинка WebGit — это запустить простенький HTTP-север webrick с помощью команды git instaweb, как показано в дампе 2.3.2
Дамп 2.3.2
user@debian-11uni:/home/git/repos$ sudo -u git git init . user@debian-11uni:/home/git/repos$ sudo -u git git instaweb --httpd=webrick -p 8080 --start
В результате, при обращении по ссылке: http://127.0.0.1:8080/ будет выполнен сервис CGI, называемый GitWeb [3.10], который запускается из корня /home/git/repos и позволяет просматривать в нем содержимое корневой директории и/или поддиректории (вложенные репозитории Git), как показано на рисунке 2.3.3
Рисунок 2.3.3
На котором показан пустой и созданный для организации просмотра /home/git/repos и два вложенных в него репозитария example.git и procps_ptree.git
При этом, все бы хорошо, но функциональность написанный на Ruby север webrick желает оставлять лучшего, т.к. запускает CGI сервер с помощью файла обертки .git/gitweb/webrick/wrapper.sh всегда обращается напрямую /usr/share/gitweb/gitweb.cgi, листинг .git/gitweb/webrick/wrapper.sh показан в листинге 2.3.4
Дамп 2.3.4
#!/bin/sh
# we use this shell script wrapper around the real gitweb.cgi since
# there appears to be no other way to pass arbitrary environment variables
# into the CGI process
GIT_EXEC_PATH=/usr/lib/git-core GIT_DIR=/home/git/repos/.git GITWEB_CONFIG=/home/git/repos/.git/gitweb/gitweb_config.perl
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
exec /usr/share/gitweb/gitweb.cgi
При этом, читающим эту статью нужно учитывать, что её автор использует Debian, который вместе с пакетом git устанавливает CGI-скрипт GitWeb в директории /usr/share/gitweb/span> (выделена жирным в листинге 2.3.4).
Поэтому для выполнения запросов из консоли с помощью клиента git(1), нам понадобится развернуть Apache, как это предлагает настоятельно руководство к WebGit, потому что для клонирования или получения порции данных от запрашиваемого проекта нужен полноценный HTTP сервер, а не запускающий один-единственный скрипт CGI, когда запускали webrick
Для этого используется CGI программа git-http-backend, которая находится в Debian находится /usr/lib/git-core/ и обеспечивает доступ к содержимому Git-репозитария по протоколам http:// и https:// . Эта программа поддерживает загрузку данных репозитария Git клиентам по обеим редакциям протокола HTTP — современной HTTP Smart и более старой — безответного, обеспечивающего обратную совместимость HTTP Dumb [
3.9].
При этом, HTTP Smart работает схожим образом c протоколами SSH и Git, но откланяется от стандарта портов HTTPS и может использовать различные механизмы аутентификации HTTP, что иногда бывает легче для пользователя чем в случае с протоколом SSH. Потому что в этом случае можно использовать аутентификационные данные — пару имя пользователя и пароль вместо того, чтобы создавать ключи SSH.
Так, если сервер не отвечает на запросы сервиса Git по протоколу HTTP Smart , клиент git попытается переключится на более простую версию протокола HTTP Dump, который предполагает, что клиент будет получать голый Git репозитарий от сервера, наподобие как тот передает обычные файлы. Для того, чтобы просто получатть голый репозитарий Git, нужно в директории с метаданными Git установить хук post-update в git/hooks/post-update . Для этого нужно просто выполнить команду переименования, как показано в дампе 2.3.5
Дамп 2.3.5
user@debian-11uni:/home/git/repos/example.git$ sudo -u git mv .git/hooks/post-update .git/hooks/post-update.sample
Теперь приступим к настройке полноценного сервера Apache с поддержкой протокола HTTP(S) . Для чего, сперва установим требуемые пакеты, как показано в дампе 2.3.6
Дамп 2.3.6
user@debian-11uni:/home/git$ sudo apt-get update user@debian-11uni:/home/git$ sudo apt-get install apache2
Как показано в дампе 2.3.6, до установки Apache произвести обновление кэша пакетов APT, обновить пакеты, если есть такие имеются.
Для запуска сервера Apache от имени пользователя git произвел соответствующие изменения в файле /etc/apache2/envvars [3.12], как показано в дампе 2.3.7
Дамп 2.3.7
... user@debian-11uni:/home/git/$ sudo nano /etc/apache2/envvars ... # Since there is no sane way to get the parsed apache2 config in scripts, some # settings are defined via environment variables and then used in apache2ctl, # /etc/init.d/apache2, /etc/logrotate.d/apache2, etc. #export APACHE_RUN_USER=www-data #export APACHE_RUN_GROUP=www-data export APACHE_RUN_USER=git export APACHE_RUN_GROUP=git ...
Выключил поддержку SSL, как показано в дампе 2.3.8
Дамп 2.3.8
$ sudo a2enmod cgi alias rewrite env ... Enabling module socache_shmcb. Enabling module ssl. ...
Apache должен быть сконфигурирован с поддержкой CGI в той директории, в которой gitweb установлен был установлен. В данном случае, такой директорией является /home/git/repos/cgi-bin/ , где /home/git/repos/ — корневая сервера WEB, а gitweb был установлен в её поддиректорию cgi-bin, как показано в дампе 2.3.9
Дамп 2.3.9
user@debian-11uni:/home/git/$ sudo -u git git clone git://git.kernel.org/pub/scm/git/git.git ./gitweb-source [sudo] пароль для user: Клонирование в «./gitweb-source»… remote: Enumerating objects: 2845, done. remote: Counting objects: 100% (2845/2845), done. remote: Compressing objects: 100% (486/486), done. remote: Total 321876 (delta 2522), reused 2543 (delta 2359), pack-reused 319031 Получение объектов: 100% (321876/321876), 105.36 MiB | 2.70 MiB/s, готово. Определение изменений: 100% (240570/240570), готово. user@debian-11uni:/home/git/$ sudo -u git mkdir -m 755 -p /home/git/repos/cgi-bin user@debian-11uni:/home/git/$ sudo -u git mkdir -m 755 -p /home/git/repos/static user@debian-11uni:/home/git/$ cd ./gitweb-source user@debian-11uni:/home/git/gitweb-source/$ sudo -u git make GITWEB_PROJECTROOT="/home/git/repos/cgi-bin" prefix=/usr gitweb GIT_VERSION = 2.35.1.46.g38062e73e0 SUBDIR gitweb SUBDIR ../ make[2]: «GIT-VERSION-FILE» не требует обновления. GEN gitweb.cgi GEN static/gitweb.js user@debian-11uni:/home/git/gitweb-source/$ sudo -u git cp -Rf gitweb/gitweb.* /home/git/repos/cgi-bin user@debian-11uni:/home/git/gitweb-source/$ sudo -u git cp -Rf gitweb/static /home/git/gitweb-static
В котором показано получение из доверенного источника GitWeb в назначенную пользователем директорию /home/git/repos/git-source и установка скриптов gitweb в CGI директорию /home/git/repos/cgi-bin/ [3.11].
После чего, приступил к настройке совместного использования WebGit с Apache. Для чего определился с со структурой удаленного репозитария, который показан в дампе 2.3.10
Дамп 2.3.10
user@debian-11uni:/home/git/repos$ exa --tree -D . . ├── cgi-bin ├── example.git ├── git-shell-commands ├── procps_ptree.git │ ├── branches │ ├── hooks │ ├── info │ ├── objects │ │ ├── info │ │ └── pack │ └── refs │ ├── heads │ └── tags └── static └── js └── lib
Поэтому в директории /home/git/ определяем файл конфигурации для скрипта gitweb.cgi, содержимое которого показано в листинге 2.3.11
Дамп 2.3.11
# gitweb configuration file for http://git.example.org # our $projectroot = "/home/git/repos" ; # FHS recommendation our $site_name = 'My locale projects'; @stylesheets = ("/static/gitweb.css"); $logo = ("/static/git-logo.png"); $favicon = ("/static/git-favicon.png"); $projects_list = $projectroot; $per_request_config = 1; $home_link = "/"; $site_header = "/home/git/gitweb-static/html-pages/siteheader.html";
После настройки конфигурационного файла webgit.conf скрипта gitweb.cgi, основные настройки Apache касаются программного компонента Rewrite engine(движок перезаписи), который выполняет процедуру перезаписи над унифицированным указателем ресурсов (Uniform Resource Locators, URLs) и используемый для реализации некой абстракции между генерирующими страницу WEB файлами и URLs, которая доступна из вне и имеющая внутреннею связь с ними. А так же откуда брать HTML код для заголовка, который состоит из картинки с эмблемой "домашних разработчиков-тихушников" и заголовка "Git Repos".
По умолчанию, Apache создает конфигурацию виртуального узла, который доступен для всех IP на порту 80, в файле /etc/apache2/sites-available/000-default.conf , поправленное содержимое которого показано в листинге 2.3.12
Дамп 2.3.12
<VirtualHost *:80> ... DocumentRoot /home/git/repos SetEnv GITWEB_CONFIG /home/git/gitweb.conf #smart http protocol # by default allows GET only, not push. # we serve it over the same URLs as the normal gitweb URLs! SetEnv GIT_PROJECT_ROOT /home/git/repos SetEnv GIT_HTTP_EXPORT_ALL ScriptAliasMatch \ "(?x)^/(.*git/(HEAD | \ info/refs | \ objects/(info/[^/]+ | \ [0-9a-f]{2}/[0-9a-f]{38} | \ pack/pack-[0-9a-f]{40}\.(pack|idx)) | \ git-(upload|receive)-pack))$" \ /usr/lib/git-core/git-http-backend/$1 ScriptAlias /git/ /var/www/cgi-bin/gitweb.cgi/ ScriptAlias /cgi-bin/ /home/git/repos/cgi-bin/ <Directory "/home/git/repos"> Options +FollowSymLinks +ExecCGI AddHandler cgi-script .cgi Require all granted DirectoryIndex /cgi-bin/gitweb.cgi RewriteEngine On # next two of the rewrite conditions on that requested file or directory exists RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^.* /gitweb.cgi/$0 [L,PT] # make access for "dumb clients" work RewriteRule ^/(.*\.git/(?!/?(HEAD|info|objects|refs)).*)?$ \ /cgi-bin/gitweb.cgi%{REQUEST_URI} [L,PT] # Make shortcut for a repostitory, so URL like <http://localhost/my_repository.git> # loads proper repository in Gitweb RewriteRule ^/(\w+\.git)$ /?p=$1 [L,P] </Directory> <Directory "/usr/lib/git-core/"> <Files "git-http-backend"> Options +ExecCGI Require all granted </Files> </Directory> <Location "/home/git/gitweb.conf"> Require all denied </Location> ...
В листинге 2.3.12, представлена конфигурация, которая поддерживает два загрузки по одному и тому же URL, который можно использовать в адресной строке браузера и клиента Git, выполняющий команды подобные git clone или git pull , как показано в дамп ее 2.3.13
Дамп 2.3.13
$ rm -rf example && git clone http://localhost/example.git Клонирование в «example»… remote: Enumerating objects: 3, done. remote: Counting objects: 100% (3/3), done. remote: Compressing objects: 100% (2/2), done. remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 Распаковка объектов: 100% (3/3), 449 bytes | 449.00 KiB/s, готово.
При этом машинка в браузере (запуск http://localhost) не хотела работать без явного указания через дерективу ScriptAlias внутреннего отображения /cgi-bin/ в URL к ее фактичекому расположению в файловой системе, директории /home/git/repos/cgi-bin/
На финальной стадии настройки выполнил активацию поддержку CGI, внутреннего отображения URL к фактическому в файловой системе и переменных окружения, которые широко используются в моей конфигурации, выполнил команду a2enmod вместе с перезапуском Apache, как показано в дампе 2.3.14
Дамп 2.3.14
sudo a2enmod cgi alias rewrite env sudo service apache2 restart
2.4 Протокол Git
Реализует модулем git-daemon, который поднимает простой сервер Git на сетевом порту 9418/tcp, как показано в дампе 2.4.1
Дамп 2.4.1
user@debian-11uni:/home/git/repos$ sudo -u git git daemon --base-path=/home/git/repos --export-all --enable=receive-pack --reuseaddr --informative-errors --verbose [282321] Ready to rumble [282331] Connection from [::1]:43496 [282331] Extended attribute "host": localhost [282331] Extended attribute "protocol": version=2 [282331] Request upload-pack for '/procps_ptree.git' [282321] [282331] Disconnected
В дампе 2.4.1 осуществляется доступ к локальному хранилищу /home/git/repos репозитария Git в режиме только чтения, и т.е. выполнения команд git clone и git pull, но для реализации записи, т.е. выполнения таких команд как git push или git remote add нужно добавить в этот набор переменных опцию --enable=receive-pack .
Для подхвата менеджером запуска Systemd, воспользовался рецептом, предложенным Джерими Си Фостер (Jeremiah C Foster)[3.18] и адаптированным мной к условиям функционирования на системах с Debian-11
Для получения GitHub Gits серверной конфигурации блока Systemd и запуска, выполните команду, показанную в дампе 2.4.1, и следуйте затем инструкциям в README.md
Дамп 2.4.2
git clone https://gist.github.com/702b7b79faf34ba3ff2194d0f404760b.git git-daemon && \ cd git-daemon
Библиография
3.1 How to Set up the HTTP Git Server for Private Projects
3.2 procps_ptree
3.3 Flawfinder
3.4How to use make and automake
3.5 Git Guide book. 4.1 Git on the Server - The Protocols
3.6 WiKiPedia. The file URI scheme
3.7 refusing to update checked out branch: refs/heads/master error #20
3.9 Git Guide book. git-http-backend
3.11 How do you clone a Git repository into a specific folder?
3.12 How to run apache as an alternate user
3.13 Git Guide book.gitweb.conf
3.14 WiKiPedia. Rewrite engine
3.16 git-http-backend on Apache 2.4
3.17 Taming The Git-Daemon To Quickly Share Git Repository
3.18 git-daemon
3.19 ARCHIVED: Lightweight Public Git Self Hosting
3.20 gitweb.conf.txt