diff --git a/.workflows/bats/Dockerfile b/.workflows/bats/Dockerfile index afbcef3..77e8616 100644 --- a/.workflows/bats/Dockerfile +++ b/.workflows/bats/Dockerfile @@ -1,15 +1,60 @@ ARG bashversion=3.2.57 +FROM alpine:3.12.0 as git +RUN apk add --no-cache \ + zlib-dev \ + openssl-dev \ + curl-dev \ + curl \ + expat-dev \ + perl-dev \ + python3-dev \ + pcre2-dev \ + asciidoc \ + xmlto \ + perl-error \ + tcl \ + tk \ + make \ + gcc \ + g++ +WORKDIR /src +ARG gitversion=2.26.2 +RUN curl --output git-${gitversion}.tar.gz \ + https://mirrors.edge.kernel.org/pub/software/scm/git/git-${gitversion}.tar.gz && \ + tar -xvzf git-${gitversion}.tar.gz +RUN export TO=/git && \ + mkdir -p ${TO} && \ + cd git-${gitversion} && \ + make prefix=/usr DESTDIR=${TO} NO_GETTEXT=YesPlease NO_REGEX=YesPlease ICONV_OMITS_BOM=Yes && \ + make prefix=/usr DESTDIR=${TO} NO_GETTEXT=YesPlease NO_REGEX=YesPlease ICONV_OMITS_BOM=Yes strip && \ + make prefix=/usr DESTDIR=${TO} NO_GETTEXT=YesPlease NO_REGEX=YesPlease ICONV_OMITS_BOM=Yes install && \ + # remove files that aren't part of standard package + rm /git/usr/libexec/git-core/git-cvs* && \ + rm /git/usr/libexec/git-core/git-daemon && \ + rm /git/usr/libexec/git-core/git-fast-import && \ + rm /git/usr/libexec/git-core/git-http-backend && \ + rm /git/usr/libexec/git-core/git-instaweb && \ + rm /git/usr/libexec/git-core/git-remote-testsvn && \ + rm /git/usr/libexec/git-core/git-shell && \ + rm /git/usr/libexec/git-core/git-svn && \ + rm /git/usr/libexec/git-core/*p4* && \ + rm /git/usr/libexec/git-core/mergetools/*p4* && \ + rm /git/usr/libexec/git-core/*email* && \ + rm /git/usr/libexec/git-core/*imap* + FROM bash:${bashversion} +ARG batsversion=v1.2.0 LABEL maintainer="Dmytro Serdiuk " \ - description="The image serves the environment for the testing of Elegant Git." -VOLUME /elegant-git + description="The image serves the environment for the testing of Elegant Git." \ + bashversion=${bashversion} \ + gitversion=${gitversion} \ + batsversion=${batsversion} WORKDIR /elegant-git +VOLUME /elegant-git ENV EG_ENABLE_TESTING true -ARG gitversion=2.26.2 -RUN apk add --no-cache git~=${gitversion} -ARG batsversion=v1.2.0 -RUN git clone --branch ${batsversion} --single-branch --depth 1 https://github.com/bats-core/bats-core.git; \ +COPY --from=git /git/usr/ /usr/ +RUN apk add --no-cache curl-dev && \ + git clone --branch ${batsversion} --single-branch --depth 1 https://github.com/bats-core/bats-core.git && \ cd bats-core && ./install.sh /usr/local && cd - && rm -r bats-core -COPY bats-workflows.bash /bats-workflows.bash -ENTRYPOINT [ "/bats-workflows.bash" ] +ENTRYPOINT [ ".workflows/bats/bats-workflows.bash" ] CMD ["help"] diff --git a/.workflows/bats/bats-workflows.bash b/.workflows/bats/bats-workflows.bash index 8380d09..fae619f 100755 --- a/.workflows/bats/bats-workflows.bash +++ b/.workflows/bats/bats-workflows.bash @@ -25,7 +25,6 @@ MESSAGE main() { case ${1} in - all_tests) all_tests ;; some_tests) shift; some_tests "${@}" ;; help) usage ;; *) "${@}" ;; diff --git a/.workflows/docs/Dockerfile b/.workflows/docs/Dockerfile index 623d799..601334b 100644 --- a/.workflows/docs/Dockerfile +++ b/.workflows/docs/Dockerfile @@ -1,16 +1,15 @@ -ARG pythonversion=3.8.3 -FROM python:${pythonversion}-alpine +FROM python:3.8.3-alpine LABEL maintainer="Dmytro Serdiuk " \ description="The image serves the environment for docs development of Elegant Git." +ARG bashversion=5.0.17 +ARG gitversion=2.26.2 +RUN apk add --no-cache bash=~${bashversion} git=~${gitversion} VOLUME /elegant-git WORKDIR /elegant-git ENV EG_ENABLE_TESTING true EXPOSE 80 -ENTRYPOINT [ "/docs-workflows.bash" ] +ENTRYPOINT [ ".workflows/docs/docs-workflows.bash" ] CMD ["help"] COPY docs/requirements.txt . -RUN python -m pip install --upgrade pip && \ - python -m pip install -r requirements.txt -ARG bashversion=5.0.17 -RUN apk add --no-cache bash~=${bashversion} -COPY .workflows/docs/docs-workflows.bash / +RUN python -m pip install --no-cache-dir --upgrade pip && \ + python -m pip install --no-cache-dir -r requirements.txt diff --git a/.workflows/docs/docs-workflows.bash b/.workflows/docs/docs-workflows.bash index 0ddca5f..70d2292 100755 --- a/.workflows/docs/docs-workflows.bash +++ b/.workflows/docs/docs-workflows.bash @@ -2,7 +2,7 @@ set -e generate() { - exec python .workflows/docs/docs.py + python .workflows/docs/docs.py } build() { @@ -10,14 +10,20 @@ build() { if test -z "${EG_ENABLE_TESTING}"; then site_directory=$(pwd)/elegnat-git-docs fi - exec python -m mkdocs build --clean --strict --site-dir ${site_directory} + python -m mkdocs build --clean --strict --site-dir ${site_directory} } preview() { exec python -m mkdocs serve --dev-addr 0.0.0.0:80 } -help() { +check() { + echo "Checking if there are uncommitted docs files ..." + git update-index --really-refresh + git diff-index --quiet HEAD docs +} + +usage() { cat < @@ -26,8 +32,17 @@ Available commands: generate generates fresh commands documentation build builds the static documentation site preview previews the documentation site + check shows whether 'docs' directory is committed or not MESSAGE } -${1} +main() { + case ${1} in + help) usage ;; + ci) generate && check ;; + *) "${@}" ;; + esac +} + +main "${@}" diff --git a/docs/index.md b/docs/index.md index b186c10..edaa31a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -21,6 +21,18 @@ picture-in-picture" allowfullscreen> > _Looks interesting? Go to [getting started](getting-started.md) guide or take a look for available [commands](commands.md)._ +Elegant Git has a rich test suit that executes on several Git and Bash versions. Please refer to +the table below to see the coverage matrix. + +`bash --version`|`git --version`|`git elegant --version` +---|---|--- +5.0.17|2.26.2|up to the latest +4.4.23|2.26.2|up to the latest +3.2.57|2.26.2|up to the latest +5.0.17|2.13.7|up to the latest +4.4.23|2.13.7|up to the latest +3.2.57|2.13.7|up to the latest + # Workflows While developing something, it may be required to format code prior to committing modifications or to open several URLs to report release notes after a new release. All these and similar actions, diff --git a/workflows b/workflows index 25187c4..f6fa44b 100755 --- a/workflows +++ b/workflows @@ -3,43 +3,49 @@ set -e source ./libexec/plugins/text -bash_3_2_57=3.2.57 +# bash versions +bash_3_2_57=3.2.57 # MACOS default +bash_4_4_23=4.4.23 bash_5_0_17=5.0.17 +# python versions python_3_8_3=3.8.3 +# git versions +git_2_13_7=2.13.7 git_2_26_2=2.26.2 -bashes=( - ${bash_5_0_17} - ${bash_3_2_57} -) -gits=( - ${git_2_26_2} -) -base_testing_worker=bash${bash_3_2_57}-git${git_2_26_2} + +--worker-tag() { + # usege: --worker-tag + echo ${1}-${2} +} eg_docker_organization=beeshive -eg_documents_repository=${eg_docker_organization}/elegant-git-docs-workflows -eg_documents_image=${eg_documents_repository}:python${python_3_8_3}-bash${bash_5_0_17} +eg_documents_repository=${eg_docker_organization}/elegant-git-docs-worker +eg_documents_bash=${bash_5_0_17} +eg_documents_git=${git_2_26_2} +eg_documents_image=${eg_documents_repository}:$(--worker-tag ${eg_documents_bash} ${eg_documents_git}) eg_bats_repository=${eg_docker_organization}/elegant-git-bats-worker -eg_bats_image=${eg_bats_repository}:${base_testing_worker} - +bats_worker_tags=( + $(--worker-tag ${bash_3_2_57} ${git_2_13_7}) + $(--worker-tag ${bash_4_4_23} ${git_2_13_7}) + $(--worker-tag ${bash_5_0_17} ${git_2_13_7}) + $(--worker-tag ${bash_3_2_57} ${git_2_26_2}) + $(--worker-tag ${bash_4_4_23} ${git_2_26_2}) + $(--worker-tag ${bash_5_0_17} ${git_2_26_2}) +) --publish-worker() { info-text "Select image to push:" - select tag in $(docker image ls --filter reference=${1} --format "{{.Repository}}:{{.Tag}}"); do + select tag in $(docker image ls --filter reference=${1} --format "{{.Repository}}:{{.Tag}}") exit; do + if test ${tag} = "exit"; then break; fi docker push ${tag} - break done } # docs workflows prepare-docs-worker() { - local bashversion=${bash_5_0_17} - local pythonversion=${python_3_8_3} docker build --tag ${eg_documents_image} \ - --label bashversion=${bashversion} \ - --label pythonversion=${pythonversion} \ - --build-arg bashversion=${bashversion} \ - --build-arg pythonversion=${pythonversion} \ + --build-arg bashversion=${eg_documents_bash} \ + --build-arg gitversion=${eg_documents_git} \ --file .workflows/docs/Dockerfile . } @@ -68,100 +74,130 @@ serve-docs() { # docs workflows repository() { - info-text "Start container..." - docker run --interactive --tty --detach --rm \ - --name repository \ - --workdir /tmp/elegant-git-repo \ - --mount type=bind,source=$(pwd),target=/elegant-git \ - ${eg_bats_image} bash - info-text "Init repository..." - docker exec --interactive --tty repository bash -c " - cd /elegant-git - source tests/addons-repo.bash - source tests/addons-common.bash - repo-new - " - info-text "Install Elegant Git..." - docker exec --interactive --tty repository bash -c " - cd /elegant-git - .workflows/installation-workflows.bash install - " - info-text "Ready! Enjoy experiments..." - docker attach repository + info-text "Select worker:" + select worker in ${bats_worker_tags[*]} exit; do + if test ${worker} = "exit"; then break; fi + info-text "Start container..." + docker run --interactive --tty --detach --rm \ + --name repository \ + --workdir /tmp/elegant-git-repo \ + --mount type=bind,source=$(pwd),target=/elegant-git \ + ${eg_bats_repository}:${worker} bash + info-text "Init repository..." + docker exec --interactive --tty repository bash -c " + cd /elegant-git + source tests/addons-repo.bash + source tests/addons-common.bash + repo-new + " + info-text "Install Elegant Git..." + docker exec --interactive --tty repository bash -c " + cd /elegant-git + .workflows/installation-workflows.bash install + " + info-text "Ready! Enjoy experiments..." + docker attach repository + done } # testing workflows prepare-bats-worker() { - info-text "Select Bash and Git version:" - select bashv in ${bashes[*]}; do - bashversion=${bashv} - break - done - select gitv in ${gits[*]}; do - gitversion=${gitv} - break - done local batsversion=v1.2.0 - docker build --tag ${eg_bats_repository}:bash${bashversion}-git${gitversion} \ - --label bashversion=${bashversion} \ - --label gitversion=${gitversion} \ - --label batsversion=${batsversion} \ - --build-arg bashversion=${bashversion} \ - --build-arg gitversion=${gitversion} \ - --build-arg batsversion=${batsversion} \ - .workflows/bats + info-text "Select worker:" + select worker in ${bats_worker_tags[*]} exit; do + if test ${worker} = "exit"; then break; fi + docker build --tag ${eg_bats_repository}:${worker} \ + --build-arg bashversion=${worker%%-*} \ + --build-arg gitversion=${worker##*-} \ + --build-arg batsversion=${batsversion} \ + .workflows/bats + done } publish-bats-worker() { --publish-worker ${eg_bats_repository} } -testing() { - echo $@ - docker run --interactive --rm \ - --mount type=bind,source=$(pwd),target=/elegant-git \ - ${eg_bats_image} some_tests "$@" +--bats-workers() { + # usage: --bats-workers [ ...] + local task=${1}; shift + for tag in ${bats_worker_tags[*]}; do + docker run --detach \ + --mount type=bind,source=$(pwd),target=/elegant-git \ + --name elegant-git-${task}-${tag} \ + ${eg_bats_repository}:${tag} "${@}" + done } -__fail() { - error-box $@ - exit 1 +testing() { + --bats-workers some-bats some_tests "$@" + --report-containerized-jobs } test-commands-docs() { - ( - generate-docs - git update-index --really-refresh - git diff-index --quiet HEAD docs - ) || __fail "'docs/commands.md' is not up to date. Please run './workflows generate-docs' and commit the changes." + docker run --detach \ + --mount type=bind,source=$(pwd),target=/elegant-git \ + --name elegant-git-test-commands-docs \ + ${eg_documents_image} ci } test-docs-site() { - { - docker run --interactive --rm \ - --mount type=bind,source=$(pwd),target=/elegant-git \ - ${eg_documents_image} build - } || __fail "Unable to build the documentation site." + docker run --detach \ + --name elegant-git-test-docs-site \ + --mount type=bind,source=$(pwd),target=/elegant-git \ + ${eg_documents_image} build } test-installation() { - ( - docker run --interactive --rm \ - --mount type=bind,source=$(pwd),target=/elegant-git \ - ${eg_bats_image} bash -c " + --bats-workers installation bash -c " .workflows/installation-workflows.bash install .workflows/installation-workflows.bash smoke-tests " - ) || __fail "Installation test is failed." } ci() { - docker run --interactive --rm \ - --mount type=bind,source=$(pwd),target=/elegant-git \ - ${eg_bats_image} all_tests + --bats-workers all-bats all_tests test-commands-docs test-docs-site test-installation + --report-containerized-jobs +} + +--report-containerized-jobs() { + info-box "Waiting for containerized jobs are completed ..." + while test -n "$(docker container ls --filter name=elegant-git --format {{.Names}})"; do + local containers=($(docker container ls --filter name=elegant-git --format {{.Names}})) + info-text "${#containers[*]} jobs still working ..." + sleep 3 + done + info-box "Reporting job statuses ..." + local passes=($(docker container ls --all --filter exited=0 --format {{.Names}})) + for passed in ${passes[*]} ; do + info-text "${passed} job is passed." + done + if test ${#passes[*]} -ne 0 ; then + info-text "Remove containers for passed jobs ..." + docker container rm -f ${passes[*]} + fi + local failures=($(docker container ls --all --filter name=elegant-git --format {{.Names}})) + for failed in ${failures[*]} ; do + error-text "${failed} job is failed." + docker logs ${failed} + done + if test ${#failures[*]} -ne 0 ; then + info-text "Remove containers for failed jobs ..." + docker container rm -f ${failures[*]} + fi + if test ${#failures[*]} -ne 0; then + error-box "${#passes[*]} jobs are passed and ${#failures[*]} are failed." + error-text "Failed jobs:" + for fail in ${failures[*]}; do + error-text "- ${fail}" + done + exit 1 + else + info-box "${#passes[*]} jobs are passed." + fi } robot() { @@ -172,6 +208,7 @@ robot() { for command_file in ${command_files[@]}; do local command_name=$(basename ${command_file}) if ! test -f tests/${command_name}.bats; then continue; fi + info-box "Run tests for ${command_file}" ( testing ${command_name} && info-text "'${command_file}' testing is passed." ) || { error-text "'${command_name}' testing is failed." smart_testing_status=failed @@ -184,6 +221,7 @@ robot() { continue # already tested fi if ! test -f tests/${file_name}; then continue; fi + info-box "Run tests in ${file_name}" ( testing ${file_name} && info-text "'${file_name}' testing is passed.") || { error-text "'${file_name}' testing is failed." smart_testing_status=failed @@ -210,8 +248,8 @@ Available commands: preview-docs runs a site with the current documentation (http://localhost) serve-docs runs 'generate-docs' and 'preview-docs' testing modifications - prepare-bats-worker builds a new '${eg_bats_image}' image - publish-bats-worker pushes the '${eg_bats_image}' image + prepare-bats-worker builds a new '${eg_bats_repository}' image + publish-bats-worker pushes the '${eg_bats_repository}' image testing runs bats tests; accepts a optional pattern for tests filtering ("${BASH_SOURCE[0]} testing work" run all tests which have the word in the test name) @@ -250,8 +288,9 @@ main() { if [[ -z ${command} ]]; then question-text "Please select a command:" echo "" - select any in ${commands[@]}; do - command=${any} + select choise in ${commands[@]} exit; do + if test ${choise} = "exit"; then exit; fi + command=${choise} if test ${command} = testing; then question-text "Please give the tests pattern: " read args @@ -263,6 +302,9 @@ main() { fi info-box "Run:" ${command} ${args:-${@}} ${command} ${args:-${@}} + if [[ ${command} =~ "test-" ]]; then + --report-containerized-jobs + fi } main ${@}