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

    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'а и даже разработчика в рамках командной строки.


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

    Share post

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 0

    Only users with full accounts can post comments. Log in, please.