install.fairie/.config/taskfiles/docker/Taskfile.yml
Brian Zalewski f42899b107 Latest
2022-12-24 15:04:59 -05:00

371 lines
14 KiB
YAML

---
version: '3'
vars:
DIND_MOUNT_MAPPING: /var/run/docker.sock:/var/run/docker.sock
DOCKER_BASE_TAG:
sh: |
if [ -f Dockerfile ] && type jq &> /dev/null; then
BASE_TAG="$(jq -r '.blueprint.dockerBaseTag' package.json)"
if [ "$BASE_TAG" != 'null' ]; then
echo "$BASE_TAG"
else
if grep -m1 "^FROM .* AS .*" Dockerfile > /dev/null; then
FIRST_LINE=$(head -n 1 Dockerfile | sed 's/.* AS \(.*\)/\1/')
if [ -n "$FIRST_LINE" ]; then
echo "$FIRST_LINE"
else
echo ""
fi
else
echo ""
fi
fi
fi
DOCKER_IMAGE:
sh: |
if type jq &> /dev/null; then
echo "{{.DOCKERHUB_PROFILE}}/$(jq -r '.blueprint.slug' package.json)"
fi
tasks:
build:
desc: Build a regular Docker image and then generate a slim build from it
summary: |
# Build the Docker Images
This task will build all of the corresponding Docker images as long as they
have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test)
created.
For regular projects, having a `Docker.test.yml` container-structure-test is enough. However,
for projects with multiple build targets, there should be a test for each target. So, if there
are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be
a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test.
hide:
sh: '[ ! -f Dockerfile ]'
log:
error: Encountered error while running Docker build sequence
start: Running Docker build sequence
success: Finished running Docker build sequence
cmds:
- task: ensure:running
- task: taskfile:deps
- |
if [ -d ./test/structure ]; then
TESTS="$(find ./test/structure -mindepth 1 -maxdepth 1 -type f -name "*.yml")"
if echo "$TESTS" | grep '.yml' > /dev/null; then
for TEST_FILE in $TESTS; do
TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')"
if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand | type' package.json)" == 'object' ]; then
if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand[$slug]' package.json)" != 'null' ]; then
task docker:build:slim -- "$TARGET"
else
.config/log warn 'The `.blueprint.dockerSlimCommand` is missing - skipping slim build and building just a regular image'
task docker:build:fat -- "$TARGET"
fi
else
if [ "$(jq --arg slug "$TARGET" '.blueprint.dockerSlimCommand' package.json)" != 'null' ]; then
task docker:build:slim -- "$TARGET"
else
task docker:build:fat -- "$TARGET"
fi
fi
done
else
.config/log error 'The `./test/structure` folder is present but there are no container-structure-tests in it. Add tests \
for each build target in the `Dockerfile` that you would like to generate an image for.'
exit 1
fi
else
.config/log info 'No `./test/structure` folder is present so assuming there is only one build target'
if [ "$(jq -r '.blueprint.dockerSlimCommand' package.json)" != null ]; then
.config/log info '`.blueprint.dockerSlimCommand` is present in `package.json` so building both a `latest` and `slim` image'
task docker:build:slim
else
task docker:build:fat
fi
fi
ensure:running:
summary: |
Ensures Docker Desktop is running if on a macOS machine.
cmds:
- |
if ! docker stats --no-stream; then
if [ -f /Applications/Docker.app ]; then
open /Applications/Docker.app
while (! docker stats --no-stream); do
echo "Waiting for Docker to launch..."
sleep 1
done
else
.config/log error 'Docker is either not running or not installed!'
fi
fi
status:
- '[[ "{{OS}}" != "darwin" ]]'
login:
deps:
- :install:software:docker
log:
error: Failed to authenticate `{{.DOCKERHUB_USER}}` with the DockerHub registry
start: Logging into DockerHub registry with `{{.DOCKERHUB_USER}}`
success: Authenticated to DockerHub registry with `{{.DOCKERHUB_USER}}`
cmds:
- |
if [ -n "$DOCKERHUB_REGISTRY_PASSWORD" ]; then
echo "$DOCKERHUB_REGISTRY_PASSWORD" | docker login -u {{.DOCKERHUB_USER}} --password-stdin
else
.config/log warn 'The `DOCKERHUB_REGISTRY_PASSWORD` is not an environment variable. This is required to login to DockerHub.'
fi
prepare:
cmds:
- task: build
status:
- '[ ! -f Dockerfile ]'
publish:
desc: Publish the Docker images (using `Docker*.test.yml` files)
hide:
sh: '[ ! -f Dockerfile ]'
summary: |
# Publish the Docker Images
This task will publish all of the corresponding Docker images as long as they
have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test)
created.
For regular projects, having a `Docker.test.yml` container-structure-test is enough. However,
for projects with multiple build targets, there should be a test for each target. So, if there
are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be
a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test.
cmds:
- |
if [ -d ./test/structure ]; then
TESTS="$(find ./test/structure -mindepth 1 -maxdepth 1 -type f -name "*.yml")"
if echo "$TESTS" | grep '.yml'; then
for TEST_FILE in $TESTS; do
TARGET="$(echo "$TEST_FILE" | sed 's/.*\/\([^\/]*\).yml$/\1/')"
task docker:publish:images -- "$TARGET"
done
else
.config/log error 'The `./test/structure` folder is present but there are no container-structure-tests in it. Add tests \
for each build target in the `Dockerfile` that you would like to generate an image for.'
exit 1
fi
else
.config/log info 'No `./test/structure` folder is present so assuming there is only one build target'
task docker:publish:images
fi
- task: pushrm
status:
- '[ ! -f Dockerfile ]'
publish:image:
vars:
SLIM_ENABLED:
sh: |
TYPE="$(jq -r '.blueprint.dockerSlimCommand | type' package.json)"
if [ "$TYPE" != 'object' ]; then
jq -r '.blueprint.dockerSlimCommand' package.json | sed 's/null/false/'
else
TARGET="{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.TARGET}}{{end}}"
jq --arg target "$TARGET" -r '.blueprint.dockerSlimCommand[$target]' package.json | sed 's/null/false/'
fi
log:
error: Failed to tag / push `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}`
start: Tagging and pushing `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}`
success: Finished uploading `{{.DOCKER_IMAGE}}:{{.TARGET_TAG}}`
cmds:
- docker tag {{.DOCKER_IMAGE}}:{{.SOURCE_TAG}} {{.DOCKER_IMAGE}}:{{.TARGET_TAG}}
- docker push {{.DOCKER_IMAGE}}:{{.TARGET_TAG}}
status:
- '[[ "{{.SOURCE_TAG}}" == "slim"* ]]'
- '[[ "{{.SLIM_ENABLED}}" == "false" ]]'
publish:images:
vars:
MAJOR_VERSION:
sh: jq -r '.version' package.json | sed 's/\..*\..*$//'
MINOR_VERSION:
sh: jq -r '.version' package.json | sed 's/^[^\.]*\.//' | sed 's/\.[^\.]*$//'
TAG_POST: '{{if ne .DOCKER_BASE_TAG .CLI_ARGS}}-{{if (contains "codeclimate" .CLI_ARGS)}}codeclimate{{else}}{{.CLI_ARGS}}{{end}}{{end}}'
TARGET: '{{if .CLI_ARGS}}{{.CLI_ARGS}}{{else}}{{.TARGET}}{{end}}'
VERSION:
sh: jq -r ".version" package.json
log:
error: An error occurred while publishing the Docker images
start: Publishing Docker images
success: Finished uploading all Docker images
cmds:
- task: publish:image
vars:
SOURCE_TAG: latest{{.TAG_POST}}
TARGET_TAG: 'v{{.VERSION}}{{.TAG_POST}}'
- task: publish:image
vars:
SOURCE_TAG: latest{{.TAG_POST}}
TARGET_TAG: 'v{{.MAJOR_VERSION}}{{.TAG_POST}}'
- task: publish:image
vars:
SOURCE_TAG: latest{{.TAG_POST}}
TARGET_TAG: v{{.MAJOR_VERSION}}.{{.MINOR_VERSION}}{{.TAG_POST}}
- task: publish:image
vars:
SOURCE_TAG: latest{{.TAG_POST}}
TARGET_TAG: latest{{.TAG_POST}}
- task: publish:image
vars:
SOURCE_TAG: slim{{.TAG_POST}}
TARGET_TAG: 'v{{.VERSION}}-slim{{.TAG_POST}}'
- task: publish:image
vars:
SOURCE_TAG: slim{{.TAG_POST}}
TARGET_TAG: 'v{{.MAJOR_VERSION}}-slim{{.TAG_POST}}'
- task: publish:image
vars:
SOURCE_TAG: slim{{.TAG_POST}}
TARGET_TAG: v{{.MAJOR_VERSION}}.{{.MINOR_VERSION}}-slim{{.TAG_POST}}
- task: publish:image
vars:
SOURCE_TAG: slim{{.TAG_POST}}
TARGET_TAG: slim{{.TAG_POST}}
pushrm:
deps:
- :install:github:docker-pushrm
- :install:software:docker
vars:
DOCKERHUB_DESCRIPTION:
sh: jq -r '.description' package.json
env:
DOCKER_PASS:
sh: echo "$DOCKERHUB_REGISTRY_PASSWORD"
DOCKER_USER: '{{.DOCKERHUB_USER}}'
cmds:
- cmd: docker pushrm {{.DOCKER_IMAGE}}
ignore_error: true
- cmd: docker pushrm --short '{{.DOCKERHUB_DESCRIPTION}}' {{.DOCKER_IMAGE}}
ignore_error: true
shell:
deps:
- :install:software:docker
- :install:software:jq
interactive: true
desc: Open the terminal of an existing Docker image
hide:
sh: '[ ! -f Dockerfile ]'
summary: |
# Shell into Docker Image
This task will start a shell session with one of the Docker images
that are currently present on machine. The task looks at the output
from `docker images` and filters the list based on the project's
expected DockerHub slug or a string, if you pass in CLI arguments.
**Displays a list of images that match the name in package.json:**
`task docker:shell`
**Displays a list of images that have the pattern abc in their name:**
`task docker:shell -- 'abc'`
vars:
IMAGE_OPTIONS:
sh: docker images | grep {{.DOCKER_IMAGE}} | sed 's/^\([^ ]*\).*$/\1/' | jq -Rsc 'split("\n")' | jq 'del(.[-1])'
prompt:
type: select
message: Which Docker image would you like to shell into?
options: '{{.IMAGE_OPTIONS}}'
answer:
cmds:
- docker run -v "${PWD}:/work" -w /work -it --entrypoint /bin/sh --rm {{.ANSWER}}
taskfile:deps:
vars:
DOCKER_TASKS_TAG:
sh: jq -r '.blueprint.dockerTasksTag' package.json
cmds:
- task: :common:util:task:tag:deps
vars:
TAG: '{{.DOCKER_TASKS_TAG}}'
status:
- '[ "{{.DOCKER_TASKS_TAG}}" == "null" ]'
test:
deps:
- :install:software:docker
desc: Perform all available tests on the Docker image
hide:
sh: '[ ! -f Dockerfile ]'
summary: |
# Test the Docker Images
This task will publish all of the corresponding Docker images as long as they
have a [container-structure-test](https://github.com/GoogleContainerTools/container-structure-test)
created.
For regular projects, having a `Docker.test.yml` container-structure-test is enough. However,
for projects with multiple build targets, there should be a test for each target. So, if there
are 2 build targets in the `Dockerfile` named `build1` and `build2` then there should be
a `Docker.build1.test.yml` and `Docker.build2.test.yml` container-structure-test.
## Comparing Slim Output to Latest Output
If the `dockerSlimCommand` is present in the `blueprint` section of `package.json`, the output from running
`npm run test:dockerslim` on every project/folder in the `test-output/` folder will be compared. The comparison
will ensure that the `slim` output matches the `latest` output. Each folder in the `test-output/` folder must
have a `package.json` file present with the `test:dockerslim` script defined under `scripts`.
If there is no `test-output/` folder, then this kind of test is skipped.
## `dockerSlimCommand` with Multiple Build Targets
If there are multiple build targets, then the `dockerSlimCommand` should be an object with keys equal to
the build targets and values equal to the appropriate DockerSlim command. If you are only using
`Docker.test.yml` then you can simply set `dockerSlimCommand` equal to the appropriate command. However,
if you have two targets named `build1` and `build2`, then your `dockerSlimCommand` should look something
like:
```json
{
"blueprint": {
"dockerSlimCommand": {
"build1": "...DockerSlim build command options here for build1",
"build2": "...DockerSlim build command options here for build2"
}
}
}
```
## CodeClimate CLI Testing
Any folder in the `test/` folder that starts with `codeclimate` gets scanned by CodeClimate CLI.
log:
error: Encountered error during Docker test sequence
start: Initiating Docker test sequence
success: Successfully completed Docker test sequence
cmds:
- task: :docker:test:container-structure-tests
- task: :docker:test:output
- task: :docker:test:codeclimate
- task: :docker:test:gitlab-runner
verify:
cmds:
- task: login
version:software:
cmds:
- |
if grep -q "CMD.\[\"--version\"\]" Dockerfile; then
VERSION=$(docker run --cap-drop=ALL -e PY_COLORS=0 --rm {{.DOCKER_IMAGE}}:latest | perl \
-pe 'if(($v)=/([0-9]+([.][0-9]+)+)/){print"$v";exit}$_=""')
if [[ $VERSION == *.*.* ]]; then
echo $VERSION
elif [[ $VERSION == *.* ]]; then
echo $VERSION.0
fi
fi