Skip to content

Commit

Permalink
Implemented CI pipeline via docker
Browse files Browse the repository at this point in the history
- Added Dockerfile
- Integrated with Codecov and Docker Hub
- Implement fixes recommended by shellcheck
- Updated documentation
  • Loading branch information
Dmytro Konstantinov authored May 10, 2019
1 parent f518213 commit 97d53af
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 41 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
.dockerignore
.git*
.git?*
bin/docker
coverage
Dockerfile

Expand Down
17 changes: 17 additions & 0 deletions .github/main.workflow
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
workflow "Codecov" {
on = "push"
resolves = [
"Publish coverage report"
]
}

action "Publish coverage report" {
uses = "./."
secrets = [
"CODECOV_TOKEN"
]
runs = "/etc/entrypoint.d/login_shell"
args = [
"bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} -R ${GITHUB_WORKSPACE} -s /home/coverage -Z"
]
}
55 changes: 37 additions & 18 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
FROM debian:stable-slim AS base
LABEL maintainer="UmkaDK <[email protected]>"
COPY ./docker-fs /
RUN cp /etc/skel/.??* /root \
RUN apt-get -y update \
&& apt-get -y install \
bash \
bc \
binutils \
jq \
libcurl4-openssl-dev \
libdw1 \
libiberty-dev \
python \
zlib1g \
&& apt-get --purge autoremove \
&& apt-get clean \
&& cp /etc/skel/.??* /root \
&& adduser \
--quiet \
--disabled-password \
Expand All @@ -12,25 +25,15 @@ RUN cp /etc/skel/.??* /root \
--gecos "" \
payload \
&& cp /etc/skel/.??* /home \
&& chown -R payload:payload /home \
&& apt-get -y update \
&& apt-get -y install \
bash \
binutils \
libcurl4-openssl-dev \
libdw1 \
libiberty-dev \
python \
zlib1g \
&& apt-get --purge autoremove \
&& apt-get clean
&& chown -R payload:payload /home /mnt
ENTRYPOINT ["/etc/entrypoint.d/login_shell"]

FROM base AS build
RUN apt-get -y update \
&& apt-get -y install \
binutils-dev \
cmake \
curl \
gcc \
g++ \
git \
Expand All @@ -52,11 +55,27 @@ RUN git clone --depth 1 --branch v36 https://github.com/SimonKagstrom/kcov.git \
&& make install
ENTRYPOINT ["/etc/entrypoint.d/login_shell"]

FROM base AS latest
COPY --from=build /usr/local /usr/local
COPY --chown=payload . /home
RUN rm -Rf /home/docker-fs \
&& chown -R payload:payload /mnt
FROM build AS clean
WORKDIR /home
COPY --chown=payload . .
RUN rm -Rf \
./docker-fs \
./bats-core \
./kcov
USER payload
RUN ./bin/test \
&& TZ=UTC git show \
--pretty=tformat:"%H%+D%+ad%n%+s" \
--date=format-local:"%c %Z" \
| head -5 > ./VERSION \
&& rm -Rf ./.git
VOLUME ["/mnt"]
ENTRYPOINT ["/etc/entrypoint.d/test_getopts_long"]

FROM clean AS final
USER root
COPY --from=build --chown=root /usr/local /usr/local
COPY --from=clean --chown=payload /home /home
USER payload
WORKDIR /mnt
VOLUME ["/mnt"]
Expand Down
34 changes: 23 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<div align="center">

![getopts_long logo](./images/logo.png)<br>
[![getopts_long logo](https://raw.githubusercontent.com/UmkaDK/getopts_long/master/images/logo.png)](#)<br>

![release branch: master](https://img.shields.io/badge/dynamic/json.svg?color=blue&label=release%20branch&query=%24.default_branch&url=https%3A%2F%2Fapi.github.com%2Frepos%2FUmkaDK%2Fgetopts_long)
![test coverage: 100%](https://img.shields.io/badge/test_coverage-100%25-brightgreen.svg)
[![donate via coinbase](https://img.shields.io/badge/donate-coinbase-gold.svg?colorB=ff8e00&logo=bitcoin)](https://commerce.coinbase.com/checkout/17ae30c2-9c3f-45fb-a911-36d01a3c81b6)
[![stable branch](https://img.shields.io/badge/dynamic/json.svg?color=lightgrey&label=stable&query=%24.default_branch&url=https%3A%2F%2Fapi.github.com%2Frepos%2FUmkaDK%2Fgetopts_long&logo=github)](https://github.com/UmkaDK/getopts_long/tree/master)
[![latest release](https://img.shields.io/badge/dynamic/json.svg?color=blue&label=release&query=%24.name&url=https%3A%2F%2Fapi.github.com%2Frepos%2FUmkaDK%2Fgetopts_long%2Freleases%2Flatest&logo=docker)](https://cloud.docker.com/repository/docker/umkadk/getopts_long)
[![test coverage](https://codecov.io/gh/UmkaDK/getopts_long/graph/badge.svg)](https://codecov.io/gh/UmkaDK/getopts_long)
[![donate link](https://img.shields.io/badge/donate-coinbase-gold.svg?colorB=ff8e00&logo=bitcoin)](https://commerce.coinbase.com/checkout/17ae30c2-9c3f-45fb-a911-36d01a3c81b6)

</div>

Expand All @@ -23,6 +24,7 @@ This function is 100% compatible with the built-in `getopts`. It is implemented
- [Installation](#installation)
- [Source the library](#source-the-library)
- [Paste the content](#paste-the-content)
- [Run in docker](#run-in-docker)
- [Usage](#usage)
- [Extended OPTSPEC](#extended-optspec)
- [Example script](#example-script)
Expand Down Expand Up @@ -66,6 +68,16 @@ An alternative method of installation is to simply copy-n-paste the content of `

This will make the function available to a single script and you will easily be able to customise it for your own needs. However, you will need to keep an eye on this repository and manually upgrade the function whenever an update is released.

### Run in docker

A more advanced way to run your script with getopts_long is to mount its directory into the getopts_long docker container:

```
docker container run --rm --init -it -v ${YOUR_SCRIPT_DIR}:/mnt umkadk/getopts_long -l
```

Your script will be available in `/mnt` directory, and getopts_long function can be sourced from `/home/lib/getopts_long.bash`.

## Usage

The syntax for `getopts_long` is the same as the syntax for the built-in `getopts`:
Expand Down Expand Up @@ -201,25 +213,25 @@ Even though I consider this function feature complete, contributions are always

### Test suite

Tests for this function live in the `./test` subdirectory of the project root. They are implemented using [BATS](https://github.com/bats-core/bats-core) (Unit Tests), and [Kcov](https://github.com/SimonKagstrom/kcov) (coverage report), and all the tools are provided by the included Dockerfile.
Tests for this function live in the `./test` subdirectory of the project root. They are implemented using [BATS](https://github.com/bats-core/bats-core) (unit tests), and [Kcov](https://github.com/SimonKagstrom/kcov) (coverage report), which are provided by the included Dockerfile.

A local docker image is built using the following command:
If required, a local docker image can be built using the following command:

```
docker image build --tag getopts_long .
docker image build --tag umkadk/getopts_long .
```

After that, executing the container with no arguments will run all tests and generate a coverage report in the `coverage` subdirectory of the projects root:
Executing the container with no arguments will run all tests and generate a coverage report in the `coverage` subdirectory of the projects root:

```
docker container run --rm --init -it -v ${PWD}:/mnt getopts_long
open ./coverage/index.html
docker container run --rm --init -it -v ${PWD}:/mnt umkadk/getopts_long
ls -l ./coverage/index.html
```

Individual tests could be run by passing a relative path of the BATS test file as an argument to `docker run` command:

```
docker container run --rm --init -it -v ${PWD}:/mnt getopts_long ./test/no_arguments.bats
docker container run --rm --init -it -v ${PWD}:/mnt umkadk/getopts_long ./test/no_arguments.bats
```


Expand Down
20 changes: 20 additions & 0 deletions bin/docker
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env bash

set -e

CONTAINER='umkadk/getopts_long'
CMD="${1}" && shift

case "${CMD}" in
'b'|'build')
if [[ -z "${*}" ]]; then docker image build --tag "${CONTAINER}" "${PWD}"
else docker image build ${@} --tag "${CONTAINER}" "${PWD}"; fi
;;
'r'|'run')
docker container run --rm --init -it -v "${PWD}:/mnt" "${CONTAINER}" "${@}"
;;
*)
echo "${0}: Unknown command -- ${cmd}" >&2
exit 1
;;
esac
70 changes: 62 additions & 8 deletions bin/test
Original file line number Diff line number Diff line change
@@ -1,18 +1,72 @@
#!/usr/bin/env bash

set -e
TEMP_DIR="$(mktemp -d -t kcov.XXXXXXXXX)"
trap "rm -Rf ${TEMP_DIR}" EXIT

bats --tap --recursive "${@:-./test}"
: "${GETOPTS_LONG_LIB:=./lib}"
: "${GETOPTS_LONG_TEST:=./test}"
: "${GETOPTS_LONG_COVERAGE:=./coverage}"

__exit() {
echo "${0}: ${1:?Missing required parameter -- error message}" >&2
exit "${2:-1}"
}

path_exists?() {
: "${1:?Missing required argument -- directory path}"
[[ -e "${1}" ]] || __exit "Path not found -- ${1}"
}

print_coverage_diff() {
: "${1:?Missing required argument -- new code coverage}"

local commit="$(head -c7 /home/VERSION)"
local coverage="$(jq -r .percent_covered "/home/coverage/coverage.json")"
local diff=$(echo "${coverage} - ${1}" | bc -l)

local label="%+.2f%% compared to ${commit}"
local color_label="\e[01;%sm%+.2f%%\e[00m compared to ${commit}"

if tty -s && [[ "${diff}" -gt '0' ]]; then printf "${color_label}" 32 "${diff}"
elif tty -s && [[ "${diff}" -lt '0' ]]; then printf "${color_label}" 31 "${diff}"
elif [[ "${diff}" == '0' ]]; then printf "unchanged compared to ${commit}"
else printf "${label}" "${diff}"; fi
}

path_exists? "${GETOPTS_LONG_LIB}"
path_exists? "${GETOPTS_LONG_TEST}"

# Run bats
TEST_DIR="${*:-${GETOPTS_LONG_TEST}}"
echo "Running tests: ${TEST_DIR} "
bats --tap --recursive "${TEST_DIR}"
[ -n "${*}" ] && exit

echo "Generating coverage report: ./coverage/index.html"
kcov --clean --include-path=./lib ${TEMP_DIR} bats --recursive ./test
# Setup exit trap
TEMP_DIR="$(mktemp -d -t kcov.XXXXXXXXX)"
trap 'rm -Rf ${TEMP_DIR}' EXIT

# Run kcov
echo "Generating coverage report: ${GETOPTS_LONG_COVERAGE}/index.html"
kcov --clean --include-path="${GETOPTS_LONG_LIB}" \
"${TEMP_DIR}" \
bats --recursive "${GETOPTS_LONG_TEST}"

# Clean up kcov report
rm ${TEMP_DIR}/bats/{bcov.css,amber.png,glass.png}
sed -i 's#"../data/bcov.css"#"data/bcov.css"#g' ${TEMP_DIR}/bats/*.html
rm "${TEMP_DIR}"/bats/{bcov.css,amber.png,glass.png}
sed -i 's#"../data/bcov.css"#"data/bcov.css"#g' "${TEMP_DIR}"/bats/*.html

# Present kcov report
cp -LR ${TEMP_DIR}/bats ./coverage
rm -Rf "${GETOPTS_LONG_COVERAGE}"
cp -LR "${TEMP_DIR}/bats" "${GETOPTS_LONG_COVERAGE}"

# Total code coverage
COVERAGE="$(jq -r .percent_covered "${GETOPTS_LONG_COVERAGE}/coverage.json")"
printf "Code covered: %s%% " "${COVERAGE}"

# Code coverage diff
if [[ -e "/.dockerenv" && "${PWD}" != "/home" ]]; then
printf "(%s)" "$(print_coverage_diff "${COVERAGE}")"
fi

# Finish code coverage stats with a new line
echo
6 changes: 3 additions & 3 deletions lib/getopts_long.bash
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
getopts_long() {
: ${1:?Missing required parameter -- long optspec}
: ${2:?Missing required parameter -- variable name}
: "${1:?Missing required parameter -- long optspec}"
: "${2:?Missing required parameter -- variable name}"

local optspec_short="${1%% *}-:"
local optspec_long="${1#* }"
Expand Down Expand Up @@ -49,6 +49,6 @@ getopts_long() {
[[ "${OPTERR}" == 0 ]] || echo "${0}: illegal option -- ${!optvar}" >&2
unset OPTARG
fi
printf -v ${optvar} '?'
printf -v "${optvar}" '?'
fi
}

0 comments on commit 97d53af

Please sign in to comment.