Универсальная работа с VCS/SCM в рамках автоматизации с FutoIn CID +6



use cases


Для некоторых современных программистов не существует систем контроля версий кроме Git, но на практике Subversion всё ещё востребован, а Mercurial имеет своих ярых сторонников. Быстрый поиск в подкрепление.


В результате DevOps'ы не монопроектных компаний встречаются с необходимостью автоматизировать работу с весьма разными системами. При этом у каждой есть свои нюансы и неизбежно появляются скрытые ошибки в сценариях, выстреливающие в самый неподходящий момент. Возникает потребность в предсказуемом поведении с минимальной "гибкостью", а не пёстрым букетом возможностей.


Интерфейс


В статье знакомства с FutoIn CID уже описывалась неявная работа с различными VCS. В версии v0.7 был добавлен явный командный интерфейс и новый функционал для автоматизации создания и слияния веток:


В общем-то сам интерфейс с комментариями ниже:


    cid vcs checkout [<vcs_ref>] [--vcsRepo=<vcs_repo>] [--wcDir=<wc_dir>]
    cid vcs commit <commit_msg> [<commit_files>...] [--wcDir=<wc_dir>]
    cid vcs merge <vcs_ref> [--no-cleanup] [--wcDir=<wc_dir>]
    cid vcs branch <vcs_ref> [--wcDir=<wc_dir>]
    cid vcs delete <vcs_ref> [--vcsRepo=<vcs_repo>] [--cacheDir=<cache_dir>] [--wcDir=<wc_dir>]
    cid vcs export <vcs_ref> <dst_dir> [--vcsRepo=<vcs_repo>] [--cacheDir=<cache_dir>] [--wcDir=<wc_dir>]
    cid vcs tags [<tag_pattern>] [--vcsRepo=<vcs_repo>] [--cacheDir=<cache_dir>] [--wcDir=<wc_dir>]
    cid vcs branches [<branch_pattern>] [--vcsRepo=<vcs_repo>] [--cacheDir=<cache_dir>] [--wcDir=<wc_dir>]
    cid vcs reset [--wcDir=<wc_dir>]
    cid vcs ismerged <vcs_ref> [--wcDir=<wc_dir>]

Если отстраниться от лозунгов об идейном превосходстве децентрализованных систем контроля версий, то в сухом остатке в разработке любого состоятельного проекта остаётся всё та же клиент-серверная модель с дополнительными плюшками на клиентской стороне. Вторым очевидным моментом является беспроблемная "эмуляция" клиент-серверной модели в распределённых системах и сложность реализации обратного. Этим и обусловлена логика работы — обязательная неявная синхронизацию с сервером.


Это подразумевает такую таблицу соответствия:


CID Git Mercurial Subversion
cid vcs checkout git clone/fetch + git checkout hg pull + hg checkout svn checkout/switch
cid vcs commit git commit + git push hg commit + hg push svn commit
cid vcs merge git merge + git push hg merge + hg push svn merge + svn commit
cid vcs branch git checkout -b + git push hg branch + hg push svn copy
cid vcs delete git branch -D + git push -f --delete hg checkout + hg commit --close-branch + hg push svn remove
cid vcs export git fetch/clone --mirror --depth=1 + git archive | tar hg archive --type files + .hg* cleanup svn export
cid vcs tags git ls-remote hg tags svn ls {repo}/tags
cid vcs branches git ls-remote hg branches svn ls {repo}/branches
cid vcs reset git merge --abort + git reset --hard hg update --clean + hg purge -I **/*.orig svn revert -R .
cid vcs ismerged git branch -r --merged HEAD hg merge --preview svn mergeinfo --show-revs eligible

Вроде бы достаточно понятно при всей лаконичности. Стоит лишь отметить, что:


  1. SVN полагается на слияние без явных указаний ревизий — "знай что делаешь".
  2. В Hg нет удаления веток, а bookmarks больше напоминают костыль.
  3. Разумеется, хоть на этих трёх системах свет клином и сошёлся, есть возможность добавить поддержку любой другой через механизм плагинов.

Немного примеров


Все примеры без лишних слов: маленький комментарий, последовательность команд как есть и наглядный результат.


> Подготовительные работы


Снова используем чистый Debian Jessie. Сделаем Git репозиторий, добавим файл README.txt и LICENSE, сделаем вторую ветку develop.


sudo apt-get install -y python-pip
sudo pip install futoin-cid

VCS_REPO_DIR=${HOME}/repo
VCS_REPO=git:${VCS_REPO_DIR}

function prep_cache_dir() {
    local repo=$1
    local cache_dir=$(echo $repo | sed -e 's,[/:@],_,g')
    [ -d $cache_dir ] && cid vcs reset --wcDir=$cache_dir 1>&2
    echo $cache_dir
}

# set -x is too verbose
function cid() {
    echo '$' cid "$@" 1>&2
    $(which cid) "$@"
}

rm ${VCS_REPO_DIR} wc $(prep_cache_dir ${VCS_REPO}) -rf
cid tool exec git -- init --bare ${VCS_REPO_DIR}
cid vcs checkout --vcsRepo=${VCS_REPO} --wcDir=wc

# Commit All
echo 'Info' > wc/README.txt
cid vcs commit 'Initial commit' --wcDir=wc

# Commit specific file(s)
echo 'License' > wc/LICENSE
cid vcs commit 'Adding license' LICENSE --wcDir=wc

cid vcs branch develop --wcDir=wc

prepare


> Job Story №1: когда начинается работа над новой фичей, требуется автоматическое создание ветки, чтобы упорядочить наименование


Примечание: подразумевается, что разработчики не могут создавать ветки самостоятельно или это не приветствуется, а в трекере неким образом срабатывает триггер.


function on_feature_start() {
    local repo="$1"
    local feature="$2"
    local cache_dir=$(prep_cache_dir $repo)

    cid vcs checkout develop --vcsRepo=$repo --wcDir=$cache_dir
    cid vcs branch feature-${feature} --wcDir=$cache_dir
}
function list_branches() {
    # Remote case
    cid vcs branches --vcsRepo=${VCS_REPO}
    # Local case
    cid vcs branches --wcDir=wc
}
on_feature_start "${VCS_REPO}" '123_one'
on_feature_start "${VCS_REPO}" '234_two'
list_branches

case #1


> Job Story №2: каждую ночь (каждый коммит), требуется сливать develop (release) ветку в ветку feature (develop), чтобы избежать расхождений и обеспечить соблюдение процесса


Примечание: такой рабочий процесс принято считать идеологически верным [и безопасным], хотя rebase делает более стерильную и понятную историю.


Подготовка веток:


cid vcs checkout develop --wcDir=wc
echo 'Info 2' > wc/README.txt
cid vcs commit 'Commit 2' --wcDir=wc

cid vcs checkout feature-234_two --wcDir=wc
echo 'Info 3' > wc/README.txt
cid vcs commit 'Conflict 3' --wcDir=wc

case #2 prepare


function sync_branches() {
    local repo="$1"
    local cache_dir=$(prep_cache_dir $repo)
    local logfile=$(realpath $2)

    cid vcs checkout develop --vcsRepo=$repo --wcDir=$cache_dir
    pushd $cache_dir

    # Release -> Develop
    echo >$logfile

    for b in $(cid vcs branches 'release*'); do
        echo -n "Merging $b into develop: " >>$logfile
        cid vcs merge $b &&             echo 'OK' >>$logfile ||             echo 'FAIL' >>$logfile
    done

    # Develop -> Feature
    for b in $(cid vcs branches 'feature*'); do
        echo -n "Merging develop into $b: " >>$logfile
        cid vcs checkout $b &&             cid vcs merge develop &&                 echo 'OK' >>$logfile ||                 echo 'FAIL' >>$logfile
    done

    popd
}

sync_branches "${VCS_REPO}" merge.log
cat merge.log

case #2


Ошибка ожидаемая — мы специально создали конфликт, а вот опечатка в ней — нет. Будет исправлено в следующем релизе.


> Job Story №3: после релиза, удалять все включённые feature ветки, чтобы не засорять пространство имён


Примечание: для Hg и SVN вообще нет ничего страшного, а вот для Git есть риск потерять коммиты — поэтому желательно иметь read-only зеркало для истории.


function remove_merged() {
    local repo="$1"
    local cache_dir=$(prep_cache_dir $repo)
    local logfile=$(realpath $2)

    cid vcs checkout develop --vcsRepo=$repo --wcDir=$cache_dir
    pushd $cache_dir

    echo >$logfile

    # Removed merged into develop
    for b in $(cid vcs branches 'feature*'); do
        if ! cid vcs ismerged $b; then
            continue
        fi

        echo -n "Removing $b: " >>$logfile
        cid vcs delete $b &&             echo 'OK' >>$logfile ||             echo 'FAIL' >>$logfile
    done

    popd
}

# Merge first branch
cid vcs checkout develop --wcDir=wc
cid vcs merge feature-123_one --wcDir=wc

# Try cleanup
remove_merged "${VCS_REPO}" delete.log
cat delete.log

case #3


> Job Story №4: с заданной периодичностью, генерировать файлы и отправлять в VCS, чтобы иметь целостную историю изменений


Примеры: а) загружаемые файлы в коммерческом блоге или новостном сайте имеют определённую ценность, для повышения целостности и эффективности архивирования крайне уместным становится Subversion б) пример постоянно обновляемых чёрных списков адресов активных атак, без участия FutoIn CID.


function update_file() {
    local repo="$1"
    local cache_dir=$(prep_cache_dir $repo)

    cid vcs checkout develop --vcsRepo=$repo --wcDir=$cache_dir
    pushd $cache_dir

    date > Timestamp.txt
    cid vcs commit 'Updated timestamp' Timestamp.txt

    popd
}
update_file "${VCS_REPO}"

case #4


> Job Story №5: каждую ночь, требуется обновлять зависимости во всех проектах, чтобы всегда использовать актуальные версии


Примечание: может быть куча других вариаций автоматизированной обработки списка проектов.


function update_project_deps() {
    local repo="$1"
    local cache_dir=$(prep_cache_dir $repo)

    cid vcs checkout develop --vcsRepo=$repo --wcDir=$cache_dir
    pushd $cache_dir

    date > Timestamp.txt

    for t in $(cid tool detect); do
        case $t in
            npm*|composer*|bundler*) cid tool exec $t -- update ;;
        esac
    done

    cid vcs commit 'Updated dependencies'

    popd
}
update_project_deps "${VCS_REPO}"

case #5
В пример не были добавлены файлы npm/composer/bundler чтобы не загромождать.


Заключение


В целом FutoIn CID не навязывает какие-то технологические процессы, а лишь предоставляет единый легковесный интерфейс к VCS/SCM для простора творчества админа, DevOps'а и даже разработчика в рамках командной строки.


Разумеется интерфейс ещё полностью не устаканился, возможны обратно совместимые изменения. Любые найденные проблемы, предложения и пожелания приветствуются.



Комментарии (0):