Skip to content

Compose

The first service we will build is our compose service.

Tip

If any of these apply to you:

  • Came in late and need to catch up
  • Messed up your files somehow and cannot figure out how to fix them
  • Just want skip to the end of this section of the workshop

then run the last command block under the Skip section at the very bottom of this page.

entypoint.sh

First, lets take a look at our entrypoint.sh file.

entrypoint.sh
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#!/usr/bin/env bash

entrypoint() {

  {
    local shellcheck_debug &&
      local shellcheck_args_string &&
      local -a shellcheck_args_array &&
      local shellcheck_rc
  } || return 1

  {
    shellcheck_debug="${SHELLCHECK_DEBUG=:false}" &&
      shellcheck_args_string="${SHELLCHECK_ARGS_STRING?SHELLCHECK_ARGS_STRING is a required variable.}" &&
      while read -r arg; do
        shellcheck_args_array+=( "${arg}" )
      done <<<"${shellcheck_args_string}"
   } || return 1

  # TODO: What should our bash regex be here?
  if [[ "${shellcheck_debug,,}" =~ ? ]]; then
    echo -e "\n[DEBUG] Calling shellcheck with args: shellcheck ${shellcheck_args_array[*]}"
  fi

  shellcheck "${shellcheck_args_array[@]}";
  # TODO: What Bash variable should $shellcheck_rc pull its value fron?
  shellcheck_rc=?;
  echo -e "\n[INFO] Shellcheck returned: ${shellcheck_rc}";
  return ${shellcheck_rc};

}

entrypoint || exit "${?}"

Solution

entrypoint.sh.solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#!/usr/bin/env bash

entrypoint() {

  {
    local shellcheck_debug &&
      local shellcheck_args_string &&
      local -a shellcheck_args_array &&
      local shellcheck_rc
  } || return 1

  {
    shellcheck_debug="${SHELLCHECK_DEBUG=:false}" &&
      shellcheck_args_string="${SHELLCHECK_ARGS_STRING?SHELLCHECK_ARGS_STRING is a required variable.}" &&
      while read -r arg; do
        shellcheck_args_array+=( "${arg}" )
      done <<<"${shellcheck_args_string}"
   } || return 1

  if [[ "${shellcheck_debug,,}" =~ true|yes|1 ]]; then
    echo -e "\n[DEBUG] Calling shellcheck with args: shellcheck ${shellcheck_args_array[*]}"
  fi

  shellcheck "${shellcheck_args_array[@]}";
  shellcheck_rc=${?};
  echo -e "\n[INFO] Shellcheck returned: ${shellcheck_rc}";
  return ${shellcheck_rc};

}

entrypoint || exit "${?}"

To apply the solution file, copy it over your workshop file.

Note: the current contents of your workshop file will be lost.

cp entrypoint.sh{.solution,}

Dockerfile

Next, lets take a look at our Dockerfile file.

Dockerfile
1
2
3
4
5
6
7
8
9
FROM docker.io/bash:5.3 AS shellcheckit-base
# TODO: Which dependency is missing?
RUN apk add bash-completion coreutils ? --no-cache
FROM shellcheckit-base AS shellcheckit-final
SHELL ["/usr/local/bin/bash"]
WORKDIR /workdir
# TODO: Where should be put our entrypoing script?
COPY ./entrypoint.sh ?
ENTRYPOINT ['/bin/entrypoint']

Solution

Dockerfile.solution
1
2
3
4
5
6
7
FROM docker.io/bash:5.3 AS shellcheckit-base
RUN apk add bash-completion coreutils shellcheck --no-cache
FROM shellcheckit-base AS shellcheckit-final
SHELL ["/usr/local/bin/bash"]
WORKDIR /workdir
COPY ./entrypoint.sh /bin/entrypoint
ENTRYPOINT ['/bin/entrypoint']

To apply the solution file, copy it over your workshop file.

Note: the current contents of your workshop file will be lost.

cp Dockerfile{.solution,}

compose.yml

Next, lets take a look at our compose.yml file.

compose.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
---

services:
  shellcheckit:
    # TODO: Set a pull_policy to something sensible
    pull_policy: ?
    # TODO: Relative to the top-level of the user's repo what directory will they most likely want to mount? 
    volumes:
      - ?:/workdir:ro
    working_dir: /workdir
    entrypoint: ["/bin/entrypoint"]

...

Solution

compose.yml.solution
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---

services:
  shellcheckit:
    pull_policy: always
    volumes:
      - ${PWD}:/workdir:ro
    working_dir: /workdir
    entrypoint: ["/bin/entrypoint"]

...

To apply the solution file, copy it over your workshop file.

Note: the current contents of your workshop file will be lost.

cp compose.yml{.solution,}

Skip

To skip this whole page, run the following code block:

toplevelrepodir="$(git rev-parse --show-toplevel)" \
&& [[ -n "${toplevelrepodir}" ]] \
&& [[ "${toplevelrepodir}" =~ pipelines-as-a-service ]] \
&& cd "${toplevelrepodir}" \
&& for solutionfile in services/compose/*.solution; do
  workshopfile="${solutionfile%.solution}"
  git restore "${solutionfile}"
  cp "${solutionfile}" "${workshopfile}"
done

Test

Now, we can test our compose service.

toplevelrepodir="$(git rev-parse --show-toplevel)" \
&&  [[ -n "${toplevelrepodir}" ]] \
&&  [[ "${toplevelrepodir}" =~ pipelines-as-a-service ]] \
&&  cd "${toplevelrepodir}" \
&&  [[ -f "shellcheckit.env" ]] \
&&  docker compose \
      -f "dev.overrides.compose.yml" \
      -f "compose.yml" \
      run \
        --rm \
        --build \
        --remove-orphans \
      shellcheckit

If everything is working properly, you should see your compose service use shellcheck to lint a copy of your entryopint.sh.

If it did not work take a little time to troubleshoot, but do not worry too much, the rest of the workshop does not rely on this part working properly on your local machine.

Next, we will build our hooks service.