Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0121172
check if ST2_VERSION refers to a released st2 version or a developmen…
winem Nov 8, 2020
243f590
Merge branch 'master' into winem-docker-tags-major-minor
winem Nov 8, 2020
0fe866e
add script to provide info about required tags
winem Nov 28, 2020
4c6b78d
Merge remote-tracking branch 'upstream/winem-docker-tags-major-minor'…
winem Nov 28, 2020
1a4d02d
finish code required to determine the expected tags to be updated
winem Nov 29, 2020
d3f0961
Add comment with the supported values of TAG_UPDATE_FLAG
winem Nov 29, 2020
05b025f
Add comment with the supported values of TAG_UPDATE_FLAG
winem Nov 29, 2020
6442e6d
Revert ST2_VERSION to 3.4dev
winem Nov 29, 2020
f411442
Fix indentation according to the code style
winem Dec 2, 2020
7a2a34f
Drop the registry variable as there's no need to have it configurable
winem Dec 2, 2020
06766e4
write the available releases directly into an bash array and don't us…
winem Dec 2, 2020
0f85472
Simplify the logic to determine the correct tags to be updated
winem Dec 2, 2020
8f052f1
Use tabs in the Makefile to avoid Missing seperator errors
winem Dec 3, 2020
f60ffd5
Don't rely on the dockerhub API and sort the list of returned tags
winem Dec 5, 2020
410a709
add usage info and fail on faulty user input
winem Dec 6, 2020
4de5dc1
Add newlines at the end of the Makefile and determine needed tag script
winem Dec 11, 2020
624c075
Support the latest tag as well
winem Dec 11, 2020
31c46c3
fix minor comparison
winem Dec 11, 2020
a768002
explain the logic of the script in additional comments
winem Dec 11, 2020
94f3b7a
add comments to explain the tagging logic and fix the logic
winem Dec 11, 2020
e8eee1e
Fix copy paste failure
winem Dec 12, 2020
26bd208
Add logic to tag the latest images and push the images with all appli…
winem Dec 12, 2020
5271e56
Add logic to distinguish between new patch releases or the latest and…
winem Dec 12, 2020
031a458
Reset Makefile to 3.4dev
winem Dec 15, 2020
5f83010
Update documentation and comments to reflect that the latest tag is a…
winem Feb 9, 2021
300f97f
Test v3.2.0
arm4b Feb 11, 2021
6d9265e
Push v3.3.0 and latest
arm4b Feb 11, 2021
387bf40
Push v3.3.0 and latest
arm4b Feb 11, 2021
55e4a22
Remove shell setting for deploy run
arm4b Feb 11, 2021
e58e5c7
Fix Makefile variables and 'TAG_UPDATE_FLAG' shell executed several t…
arm4b Feb 11, 2021
5657921
Test 3.2.0
arm4b Feb 11, 2021
4e15921
Revert bash for Deploy stage
arm4b Feb 11, 2021
516d2a7
Don't return latest as element in the array of available releases
winem Feb 11, 2021
8a9b666
Don't set the update flag to 3 if parsing of the available releases f…
winem Feb 11, 2021
a3b226a
Merge branch 'winem-docker-tags-major-minor' of pgithub.com:winem/st2…
winem Feb 12, 2021
beaed9e
Add additional error handling and validate variables before referring…
winem Feb 13, 2021
fbf9dae
Add quotes to fix variable parsing when checking if missing_packages …
winem Feb 15, 2021
1123837
Log an error from the makefile if no tags can be added due to errors …
winem Feb 15, 2021
cfcfb97
Improve the error handling on the tags scirpt and prefix all errors w…
winem Feb 16, 2021
da156e9
Commit to trigger a CI run for 3.3.0 (an actual release)
winem Feb 16, 2021
570a3a8
Fail the make if determining tags fails
winem Feb 16, 2021
5992a5e
Add curl and jq on the circleci node
winem Feb 16, 2021
bf6adac
Try to fix the curl relocating error
winem Feb 16, 2021
49b20b6
Still trying to fix the curl relocating error
winem Feb 16, 2021
7c7574c
Change the Makefile to 3.4dev again
winem Feb 16, 2021
4063f57
Make 3.3.0 again for a first test with the duplicate code being moved…
winem Feb 16, 2021
f4dd705
Build for 3.4dev again
winem Feb 16, 2021
f69ce15
Test st2 3.2.1
arm4b Feb 17, 2021
3567e4f
Test st2 3.2.0
arm4b Feb 17, 2021
4fb24f9
Revert to 3.4dev
arm4b Feb 17, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ jobs:
docker_layer_caching: false
- run:
name: Install Dependencies
command: apk add make bash
command: apk add make bash curl jq
- run:
name: Update curl
command: apk --upgrade --no-cache add curl
- run:
name: Build Docker images
command: make build
Expand Down
75 changes: 73 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
ST2_VERSION ?= 3.4dev
DOCKER_TAG ?= ${ST2_VERSION}
RELEASE_TAG_REGEX := [^dev]$$
SHELL := /bin/bash

# supported values of the TAG_UPDATE_FLAG
# 0 = no additional tags to be set
# 1 = add the major.minor tag
# 2 = add the tags major and major.minor
# 3 = add the tags major, major.minor and latest
TAG_UPDATE_FLAG := $(shell ./determine_needed_tags.sh st2 ${ST2_VERSION})
Copy link
Member

@arm4b arm4b Feb 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think would be best to stop the entire process at early stage once we understood that script failed instead of waiting for the entire Docker build.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now all failures in the script should be handled and return a message with the "Error:"-Prefix and the makefile checks the output for the Error-Prefix and fails before building any images.

Another idea was to introduce another return value like -1 in case of an error but then I wouldn't know how to show the error message in the CI logs. If you have an idea to show the output from the script in the CI log please let me know.

I just pushed a Makefile with 3.3.0 and will change it back to 3.4dev before merging this branch. That's just because the determine tags script is skipped if we build a *dev version and not a release.

Copy link
Member

@arm4b arm4b Feb 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Depending on "Error" message as a failure indicator adds some knowledge overhead in requirements that could be broken in the future. What if 3rd Maintainer comes, edit the determine_needed_tags.sh script and doesn't add "Error" message for the failure case.

We can at least reinforce it with determine_needed_tags.sh || echo Error to fit your solution.


ifneq ($(shell echo ${ST2_VERSION} | grep -E "${RELEASE_TAG_REGEX}"), )
RELEASE_VERSION := true
MAJOR := $(word 1, $(subst ., ,${ST2_VERSION}))
MINOR := $(word 2, $(subst ., ,${ST2_VERSION}))
PATCH := $(word 3, $(subst ., ,${ST2_VERSION}))
else
RELEASE_VERSION := false
endif

# Build all required images (st2 base image plus st2 components)
.PHONY: build
build:
build: verify_tag_update_flag
@docker build \
--pull \
--no-cache \
Expand All @@ -20,13 +37,67 @@ build:
$$component/; \
echo -e "\033[32mSuccessfully built \033[1mstackstorm/$$component:${DOCKER_TAG}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
done
ifeq ($(RELEASE_VERSION), true)
ifeq ($(TAG_UPDATE_FLAG), 1)
for image in st2 st2*; do \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:${MAJOR}.${MINOR}; \
echo -e "\033[32mSuccessfully tagged \033[1mstackstorm/$$image:${DOCKER_TAG}\033[0m\033[32m with \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[0m"; \
done
else ifeq ($(TAG_UPDATE_FLAG), 2)
for image in st2 st2*; do \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:${MAJOR}; \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:${MAJOR}.${MINOR}; \
echo -e "\033[32mSuccessfully tagged \033[1mstackstorm/$$image:${DOCKER_TAG}\033[0m\033[32m with \033[1mstackstorm/$$image:${MAJOR}\033[0m\033[32m and \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[0m"; \
done
else ifeq ($(TAG_UPDATE_FLAG), 3)
for image in st2 st2*; do \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:${MAJOR}; \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:${MAJOR}.${MINOR}; \
docker tag stackstorm/$$image:${DOCKER_TAG} stackstorm/$$image:latest; \
echo -e "\033[32mSuccessfully tagged \033[1mstackstorm/$$image:${DOCKER_TAG}\033[0m\033[32m with \033[1mstackstorm/$$image:${MAJOR}\033[0m\033[32m, \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[32m and \033[1mstackstorm/$$image:latest\033[0m"; \
done
endif
endif

.PHONY: push
push:
push: verify_tag_update_flag
docker push stackstorm/st2:${DOCKER_TAG};
@echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/st2:${DOCKER_TAG}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m";
@set -e; \
for component in st2*; do \
docker push stackstorm/$$component:${DOCKER_TAG}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$component:${DOCKER_TAG}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
done
ifeq ($(RELEASE_VERSION), true)
ifeq ($(TAG_UPDATE_FLAG), 1)
for image in st2 st2*; do \
docker push stackstorm/$$image:${MAJOR}.${MINOR}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
done
else ifeq ($(TAG_UPDATE_FLAG), 2)
for image in st2 st2*; do \
docker push stackstorm/$$image:${MAJOR}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:${MAJOR}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
docker push stackstorm/$$image:${MAJOR}.${MINOR}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
done
else ifeq ($(TAG_UPDATE_FLAG), 3)
for image in st2 st2*; do \
docker push stackstorm/$$image:${MAJOR}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:${MAJOR}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
docker push stackstorm/$$image:${MAJOR}.${MINOR}; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:${MAJOR}.${MINOR}\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
docker push stackstorm/$$image:latest; \
echo -e "\033[32mSuccessfully pushed \033[1mstackstorm/$$image:latest\033[0m\033[32m Docker image for StackStorm version \033[1m${ST2_VERSION}\033[0m"; \
done
endif
endif

verify_tag_update_flag:
ifeq ($(RELEASE_VERSION), true)
ifneq ($(shell echo "${TAG_UPDATE_FLAG}" | grep -E "Error:"),)
@echo -e "Failed to identify the tags to be set."
@echo -e "\033[31mNo images were tagged due to an error when determining the correct tags: ${TAG_UPDATE_FLAG}\033[0m"
exit 1
endif
endif
162 changes: 162 additions & 0 deletions determine_needed_tags.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#!/usr/bin/env bash

# the first argument must be the name of the container, i.e. st2, st2actionrunner, st2stream
component=$1
# the 2nd argument is the version of the current build and expects at least major.miinor to be provided
# i.e. 3.3.0, 3.3, 2.10.5
build_version=$2

showHelp() {
echo "Use this script to determine the right tags to be attached to your new image."
echo
echo "Syntax: $0 st2component image_version"
echo "st2component = an valid image of an existing stackstorm component (i.e. st2, st2api, st2stream)"
echo "image_version = the st2 release used for your current build (i.e. 3.3.0)"
echo
echo "Possible output:"
echo "0 = no additional tags to be updated"
echo "1 = update the major.minor tag (i.e. 3.3)"
echo "2 = update the major and the major.minor tag (i.e. 3 and 3.3)"
echo "3 = update the major and the major.minor tag (i.e. 3 and 3.3) as well as the latest tag"
echo
echo "Run $0 -h|--help to show this usage information."
}

case $1 in
-h|--help)
showHelp
exit
;;
esac

if [ "$#" -ne 2 ]; then
echo "Error: Missing or unexpected number of positional arguments. Expected: 2"
showHelp
exit 1
fi

# check for dependencies
missing_packages=""
for dep in curl jq; do
if ! which $dep > /dev/null; then
missing_packages+=" $dep"
fi
done

if [ ! -z "${missing_packages}" ]; then
echo "Error: Requirement(s) not satisfied: ${missing_packages}"
exit 1
fi

if [[ ${build_version} =~ ^([0-9]+)\.([0-9]+).?([0-9]*)$ ]]; then
build_major=${BASH_REMATCH[1]}
build_minor=${BASH_REMATCH[2]}
build_patch=${BASH_REMATCH[3]}
else
echo "Error: The provided version ${build_version} does not have the expected format."
exit 1
fi

if [ -z ${build_patch} ]; then
build_patch=0
fi

tag_update_flag=0
# possible values of the tag_update flag:
# 0 = no additional tags to be set
# (this applies just in case of builds for older releases while a newer minor or minor.patch is already available)
# 1 = add the major.minor tag
# (this applies, if the build is for is just for a new patch version)
# 2 = add the tags major and major.minor
# (this applies if the build is for a version equal to or greater than the latest minor of a major)
# 3 = add the tags latest in addition to the tags from #2
# (this applies if the build with the major, minor and patch version being equal to or greater than the currently latest version
# or if the build is for a completely new st2 component)

# check if there are already images for the given component available at dockerhub
dockerhub_registry_status_code=$(curl -sfL -o /dev/null -w "%{http_code}" https://registry.hub.docker.com/v1/repositories/stackstorm/${component}/tags)

if [ ${dockerhub_registry_status_code} -eq 404 ]; then
# there is no repository stackstorm/${component} available at dockerhub -> this build is for a new st2 component
tag_update_flag=3
exit
elif [ ${dockerhub_registry_status_code} -ne 200 ]; then
echo "Error: Unexpected HTTP statuscode for https://registry.hub.docker.com/v1/repositories/stackstorm/${component}/tags: ${dockerhub_registry_status_code}"
exit 1
fi

# dockerhub lists the tags in ascending order. 1st object = lowest tag; last object = highest tag or latest
docker_tags_json=$(curl -s https://registry.hub.docker.com/v1/repositories/stackstorm/${component}/tags)
readarray -t available_releases < <(echo $docker_tags_json | jq -r '.[] | select((.name | endswith("dev") | not) and (.name=="latest" | not)).name' | sort)

# sort returns the list with the highest tag or "latest" as last item
# so change the value below to -2 when introducing the tag latest to get i.e. 3.3.0 instead of latest
latest_release=${available_releases[-1]}
latest_release_array=(${latest_release//\./ })

latest_major=${latest_release_array[0]}
latest_minor=${latest_release_array[1]}

# validate the value stored as latest_release
if [[ ! ${latest_release} =~ ^([0-9]+)\.([0-9]+).?([0-9]*)$ ]]; then
echo "Error: Unexpected error. The latest release ${latest_release} does not match the expected format."
fi

if [ ${build_version} == ${latest_release} ]; then
# building a release of the latest st2 version
tag_update_flag=3
else
# building a release for a st2 version that does not match the latest version
if [ ${build_major} -le ${latest_major} ]; then
Copy link
Member

@arm4b arm4b Feb 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is what I got when running the script locally:

$ ./determine_needed_tags.sh st2 3.0.0

./determine_needed_tags.sh: line 95: [: latest: integer expression expected
3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ofc, that shouldn't happen. I'll take a look at it tomorrow evening latest and try to provide a fix on the weekend. Will also see how this passed my own tests then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok, I see how this happened. It looks like there are images at Dockerhub that do already have the latest tag and this one is now provided as the last item in the list (and no longer the actual latest release i.e. 3.3.0).

I'll address that shortly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, this is fixed now. The main issue was the one I already described above: the script expected a release version like 3.3.0 and not latest. Now, latest as available release is also filtered and no longer returned by jq.

The more important point is your 2nd one:

./determine_needed_tags.sh: line 95: [: latest: integer expression expected
3

This was a glidge in the script due to the "wonderful" (sorry, don't find an appropriate word for it right now :) ) error handling in bash.

What happend is that the if clause failed due to latest not being an integer and falling back to the else clause. I addressed this by changing:

  if [ ${build_major} -le ${latest_major} ]; then # < this one failed
[...]
  else # < so we got here
    # building a release for a new major version
    tag_update_flag=3
  fi

to:

  if [ ${build_major} -le ${latest_major} ]; then
[...]
  elif [ ${build_major} -gt ${latest_major} ]; then
    # building a release for a new major version
    tag_update_flag=3
  fi

So the worst thing that could happen now if parsing of a returned tag leads to an unwanted result is the error below:

./determine_needed_tags.sh: line 95: [: latest: integer expression expected
./determine_needed_tags.sh: line 141: [: latest: integer expression expected
0

So the script returns 0 and no tag would be updated. I'll see if there is a more elegant solution to address this and even avoid (=handle) the 2 error messages.

# building a release for an older major
readarray -t build_version_major_minor_matching_releases < <(echo $docker_tags_json | jq -r '.[] | select(.name | endswith("dev") | not) | select(.name | startswith("'"${build_major}.${build_minor}"'")).name')
if [ ${#build_version_major_minor_matching_releases[@]} -ge 1 ]; then
# at least one version matching the current builds major and minor version is available
latest_build_version_major_minor_matching_release=${build_version_major_minor_matching_releases[-1]}
latest_build_version_major_minor_matching_release_array=(${latest_build_version_major_minor_matching_release//\./ })
latest_build_version_major_minor_matching_major=${latest_build_version_major_minor_matching_release_array[0]}
latest_build_version_major_minor_matching_minor=${latest_build_version_major_minor_matching_release_array[1]}
latest_build_version_major_minor_matching_patch=${latest_build_version_major_minor_matching_release_array[2]}
if [ ${build_minor} -eq ${latest_minor} ] && \
[ ${build_minor} -eq ${latest_build_version_major_minor_matching_minor} ] && \
[ ${build_patch} -ge ${latest_build_version_major_minor_matching_patch} ]; then
# building a release for a new or updated patch version of the current major.minor version
tag_update_flag=3
elif [ ${build_minor} -eq ${latest_build_version_major_minor_matching_minor} ] && \
[ ${build_patch} -ge ${latest_build_version_major_minor_matching_patch} ]; then
# building a release for a new or updated patch version of an old major.minor version
readarray -t build_version_major_matching_releases < <(echo $docker_tags_json | jq -r '.[] | select(.name | endswith("dev") | not) | select(.name | startswith("'"${build_major}"'")).name')
latest_build_version_major_matching_release=${build_version_major_matching_releases[-1]}
latest_build_version_major_matching_release_array=(${latest_build_version_major_matching_release//\./ })
latest_build_version_major_matching_minor=${latest_build_version_major_matching_release_array[1]}
if [ ${build_minor} -ge ${latest_build_version_major_matching_minor} ]; then
# building a release for a new or updated patch version of the latest or a new minor version of the major release
tag_update_flag=2
else
# building a release for an older minor version of the major release
tag_update_flag=1
fi
elif [ ${build_minor} -lt ${latest_minor} ] && [ ${build_patch} -ge ${latest_build_version_major_minor_matching_patch} ]; then
# building a patch release for an older minor version
tag_update_flag=1
fi
elif [ ${build_minor} -gt ${latest_minor} ]; then
# building a release of a new minor version of the current or an old major version
if [ ${build_major} -eq ${latest_major} ]; then
# building a release of a new minor for the latest major version
tag_update_flag=3
else
# building a release for a new minor of an old major version
tag_update_flag=2
fi
#else
# # building a release for an old, unreleased major version of st2
# tag_update_flag=2
fi
elif [ ${build_major} -gt ${latest_major} ]; then
# building a release for a new major version
tag_update_flag=3
fi
fi

echo $tag_update_flag