--- 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